Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/stefan-niedermann/nextcloud-deck.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/PickStackActivity.java86
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java13
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/pickstack/PickStackViewModel.java53
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java62
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateViewModel.java104
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/stack/StackWidgetConfigurationActivity.java16
-rw-r--r--app/src/main/res/layout/activity_pick_stack.xml54
-rw-r--r--app/src/main/res/values/strings.xml3
-rw-r--r--app/src/test/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateViewModelTest.java151
9 files changed, 487 insertions, 55 deletions
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/PickStackActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/PickStackActivity.java
index d60ce4002..34df713c7 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/PickStackActivity.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/PickStackActivity.java
@@ -3,6 +3,9 @@ package it.niedermann.nextcloud.deck.ui;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.View;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
@@ -17,11 +20,14 @@ import java.util.List;
import it.niedermann.android.util.ColorUtil;
import it.niedermann.nextcloud.deck.DeckLog;
import it.niedermann.nextcloud.deck.R;
+import it.niedermann.nextcloud.deck.api.IResponseCallback;
import it.niedermann.nextcloud.deck.databinding.ActivityPickStackBinding;
import it.niedermann.nextcloud.deck.model.Account;
import it.niedermann.nextcloud.deck.model.Board;
import it.niedermann.nextcloud.deck.model.Stack;
import it.niedermann.nextcloud.deck.ui.branding.Branded;
+import it.niedermann.nextcloud.deck.ui.branding.BrandingUtil;
+import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment;
import it.niedermann.nextcloud.deck.ui.exception.ExceptionHandler;
import it.niedermann.nextcloud.deck.ui.pickstack.PickStackFragment;
import it.niedermann.nextcloud.deck.ui.pickstack.PickStackListener;
@@ -34,13 +40,8 @@ import static it.niedermann.nextcloud.deck.util.DeckColorUtil.contrastRatioIsSuf
public abstract class PickStackActivity extends AppCompatActivity implements Branded, PickStackListener {
- protected ActivityPickStackBinding binding;
- protected PickStackViewModel viewModel;
-
- private Account selectedAccount;
- private Board selectedBoard;
- private Stack selectedStack;
-
+ private ActivityPickStackBinding binding;
+ private PickStackViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -71,20 +72,58 @@ public abstract class PickStackActivity extends AppCompatActivity implements Bra
.commit();
});
binding.cancel.setOnClickListener((v) -> finish());
- binding.submit.setOnClickListener((v) -> onSubmit(selectedAccount, selectedBoard.getLocalId(), selectedStack.getLocalId()));
+ binding.submit.setOnClickListener((v) -> {
+ viewModel.setSubmitInProgress(true);
+ onSubmit(viewModel.getAccount(), viewModel.getBoardLocalId(), viewModel.getStackLocalId(), new IResponseCallback<Void>() {
+ @Override
+ public void onResponse(Void response) {
+ runOnUiThread(() -> viewModel.setSubmitInProgress(false));
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+ IResponseCallback.super.onError(throwable);
+ runOnUiThread(() -> {
+ viewModel.setSubmitInProgress(false);
+ ExceptionDialogFragment
+ .newInstance(throwable, viewModel.getAccount())
+ .show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ });
+ }
+ });
+ });
+ viewModel.submitButtonEnabled().observe(this, (enabled) -> binding.submit.setEnabled(enabled));
+ if (requireContent()) {
+ viewModel.setContentIsSatisfied(false);
+ binding.inputWrapper.setVisibility(View.VISIBLE);
+ binding.input.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // Nothing to do here...
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ viewModel.setContentIsSatisfied(s != null && !s.toString().trim().isEmpty());
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ // Nothing to do here...
+ }
+ });
+ } else {
+ viewModel.setContentIsSatisfied(true);
+ }
}
@Override
public void onStackPicked(@NonNull Account account, @Nullable Board board, @Nullable Stack stack) {
- this.selectedAccount = account;
- this.selectedBoard = board;
- this.selectedStack = stack;
- if (board == null) {
- binding.submit.setEnabled(false);
- } else {
- applyBrand(board.getColor());
- binding.submit.setEnabled(stack != null);
- }
+ viewModel.setSelected(account, board, stack);
+ applyBrand(board == null
+ ? ContextCompat.getColor(this, R.color.accent)
+ : board.getColor()
+ );
}
@Override
@@ -96,12 +135,23 @@ public abstract class PickStackActivity extends AppCompatActivity implements Bra
DrawableCompat.setTintList(binding.submit.getBackground(), ColorStateList.valueOf(finalMainColor));
binding.submit.setTextColor(ColorUtil.INSTANCE.getForegroundColorForBackgroundColor(finalMainColor));
binding.cancel.setTextColor(getSecondaryForegroundColorDependingOnTheme(this, mainColor));
+ BrandingUtil.applyBrandToEditTextInputLayout(mainColor, binding.inputWrapper);
} catch (Throwable t) {
DeckLog.logError(t);
}
}
- abstract protected void onSubmit(Account account, long boardLocalId, long stackId);
+ abstract protected void onSubmit(Account account, long boardLocalId, long stackId, @NonNull IResponseCallback<Void> callback);
abstract protected boolean showBoardsWithoutEditPermission();
+
+ protected boolean requireContent() {
+ return false;
+ }
+
+ @NonNull
+ protected String getContent() {
+ final Editable text = binding.input.getText();
+ return text == null ? "" : text.toString();
+ }
} \ No newline at end of file
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java
index 989fbcafe..71725c88a 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java
@@ -325,18 +325,7 @@ public class EditActivity extends AppCompatActivity {
applyBrandToPrimaryTabLayout(mainColor, binding.tabLayout);
}
- @NonNull
- public static Intent createNewCardIntent(@NonNull Context context, @NonNull Account account, Long boardLocalId, Long stackId, @Nullable String title, @Nullable String description) {
- return createNewCardIntent(context, account, boardLocalId, stackId, title)
- .putExtra(BUNDLE_KEY_DESCRIPTION, description);
- }
-
- @NonNull
- public static Intent createNewCardIntent(@NonNull Context context, @NonNull Account account, Long boardLocalId, Long stackId, @Nullable String title) {
- return createNewCardIntent(context, account, boardLocalId, stackId)
- .putExtra(BUNDLE_KEY_TITLE, title);
- }
-
+ @Deprecated
@NonNull
public static Intent createNewCardIntent(@NonNull Context context, @NonNull Account account, Long boardLocalId, Long stackId) {
return createBasicIntent(context, account, boardLocalId)
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/pickstack/PickStackViewModel.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/pickstack/PickStackViewModel.java
index cc9fc2259..f9fbcae89 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/pickstack/PickStackViewModel.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/pickstack/PickStackViewModel.java
@@ -3,8 +3,10 @@ package it.niedermann.nextcloud.deck.ui.pickstack;
import android.app.Application;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
import java.util.List;
@@ -13,16 +15,67 @@ import it.niedermann.nextcloud.deck.model.Board;
import it.niedermann.nextcloud.deck.model.Stack;
import it.niedermann.nextcloud.deck.persistence.sync.SyncManager;
+import static androidx.lifecycle.Transformations.distinctUntilChanged;
+
@SuppressWarnings("WeakerAccess")
public class PickStackViewModel extends AndroidViewModel {
private final SyncManager syncManager;
+ private Account selectedAccount;
+ @Nullable
+ private Board selectedBoard;
+ @Nullable
+ private Stack selectedStack;
+ private boolean contentIsSatisfied = false;
+ private boolean saveInProgress = false;
+
+ private final MutableLiveData<Boolean> submitButtonEnabled$ = new MutableLiveData<>(false);
+
public PickStackViewModel(@NonNull Application application) {
super(application);
this.syncManager = new SyncManager(application);
}
+ public LiveData<Boolean> submitButtonEnabled() {
+ return distinctUntilChanged(submitButtonEnabled$);
+ }
+
+ public void setContentIsSatisfied(boolean isSatisfied) {
+ this.contentIsSatisfied = isSatisfied;
+ this.submitButtonEnabled$.setValue(!saveInProgress && contentIsSatisfied && selectedBoard != null && selectedStack != null);
+ }
+
+ public void setSelected(@NonNull Account account, @Nullable Board board, @Nullable Stack stack) {
+ this.selectedAccount = account;
+ this.selectedBoard = board;
+ this.selectedStack = stack;
+ this.submitButtonEnabled$.setValue(!saveInProgress && contentIsSatisfied && selectedBoard != null && selectedStack != null);
+ }
+
+ public void setSubmitInProgress(boolean saveInProgress) {
+ this.saveInProgress = saveInProgress;
+ this.submitButtonEnabled$.setValue(!saveInProgress && contentIsSatisfied && selectedBoard != null && selectedStack != null);
+ }
+
+ public Account getAccount() {
+ return this.selectedAccount;
+ }
+
+ public long getBoardLocalId() {
+ if (this.selectedBoard == null) {
+ throw new IllegalStateException("Check submitButtonEnabled() before calling this method.");
+ }
+ return this.selectedBoard.getLocalId();
+ }
+
+ public long getStackLocalId() {
+ if (this.selectedStack == null) {
+ throw new IllegalStateException("Check submitButtonEnabled() before calling this method.");
+ }
+ return this.selectedStack.getLocalId();
+ }
+
public LiveData<Boolean> hasAccounts() {
return syncManager.hasAccounts();
}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java
index 53e25a0d9..ccb7c8a38 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java
@@ -1,13 +1,20 @@
package it.niedermann.nextcloud.deck.ui.preparecreate;
+import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
+import android.text.TextUtils;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
+import androidx.lifecycle.ViewModelProvider;
import it.niedermann.nextcloud.deck.R;
+import it.niedermann.nextcloud.deck.api.IResponseCallback;
import it.niedermann.nextcloud.deck.model.Account;
+import it.niedermann.nextcloud.deck.model.full.FullCard;
import it.niedermann.nextcloud.deck.ui.PickStackActivity;
import it.niedermann.nextcloud.deck.ui.card.EditActivity;
@@ -17,6 +24,8 @@ import static it.niedermann.nextcloud.deck.DeckApplication.saveCurrentStackId;
public class PrepareCreateActivity extends PickStackActivity {
+ private PrepareCreateViewModel viewModel;
+
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -24,29 +33,60 @@ public class PrepareCreateActivity extends PickStackActivity {
if (actionBar != null) {
actionBar.setTitle(R.string.add_card);
}
+
+ viewModel = new ViewModelProvider(this).get(PrepareCreateViewModel.class);
}
@Override
- protected void onSubmit(Account account, long boardId, long stackId) {
- final Intent intent = getIntent();
- if (intent == null) {
- startActivity(EditActivity.createNewCardIntent(this, account, boardId, stackId));
+ protected void onSubmit(Account account, long boardId, long stackId, @NonNull IResponseCallback<Void> callback) {
+ Toast.makeText(this, R.string.saving_new_card, Toast.LENGTH_SHORT).show();
+ final FullCard fullCard;
+ if (requireContent()) {
+ fullCard = viewModel.createFullCard(account.getServerDeckVersionAsObject(), getContent());
} else {
- startActivity(EditActivity.createNewCardIntent(this, account, boardId, stackId,
+ final Intent intent = getIntent();
+ if (intent == null) {
+ throw new IllegalStateException("Intent should not be null because title is required.");
+ }
+ fullCard = viewModel.createFullCard(
+ account.getServerDeckVersionAsObject(),
+ intent.getStringExtra(Intent.EXTRA_SUBJECT),
intent.getStringExtra(Intent.EXTRA_TITLE),
- intent.getStringExtra(Intent.EXTRA_TEXT)));
+ intent.getStringExtra(Intent.EXTRA_TEXT)
+ );
}
- saveCurrentAccount(this, account);
- saveCurrentBoardId(this, account.getId(), boardId);
- saveCurrentStackId(this, account.getId(), boardId, stackId);
- applyBrand(account.getColor());
+ viewModel.saveCard(account.getId(), boardId, stackId, fullCard, new IResponseCallback<FullCard>() {
+ @Override
+ public void onResponse(FullCard response) {
+ saveCurrentAccount(PrepareCreateActivity.this, account);
+ saveCurrentBoardId(PrepareCreateActivity.this, account.getId(), boardId);
+ saveCurrentStackId(PrepareCreateActivity.this, account.getId(), boardId, stackId);
- finish();
+ callback.onResponse(null);
+ startActivity(EditActivity.createEditCardIntent(PrepareCreateActivity.this, account, boardId, response.getLocalId()));
+ finish();
+ }
+
+ @Override
+ @SuppressLint("MissingSuperCall")
+ public void onError(Throwable throwable) {
+ callback.onError(throwable);
+ }
+ });
}
@Override
protected boolean showBoardsWithoutEditPermission() {
return false;
}
+
+ @Override
+ protected boolean requireContent() {
+ final Intent intent = getIntent();
+ return intent == null || (TextUtils.isEmpty(intent.getStringExtra(Intent.EXTRA_SUBJECT)) &&
+ TextUtils.isEmpty(intent.getStringExtra(Intent.EXTRA_TITLE)) &&
+ TextUtils.isEmpty(intent.getStringExtra(Intent.EXTRA_TEXT)));
+ }
+
} \ No newline at end of file
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateViewModel.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateViewModel.java
new file mode 100644
index 000000000..1ce61f380
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateViewModel.java
@@ -0,0 +1,104 @@
+package it.niedermann.nextcloud.deck.ui.preparecreate;
+
+import android.app.Application;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.AndroidViewModel;
+
+import it.niedermann.nextcloud.deck.api.IResponseCallback;
+import it.niedermann.nextcloud.deck.model.Card;
+import it.niedermann.nextcloud.deck.model.full.FullCard;
+import it.niedermann.nextcloud.deck.model.ocs.Version;
+import it.niedermann.nextcloud.deck.persistence.sync.SyncManager;
+
+@SuppressWarnings("WeakerAccess")
+public class PrepareCreateViewModel extends AndroidViewModel {
+
+ private final SyncManager syncManager;
+
+ public PrepareCreateViewModel(@NonNull Application application) {
+ super(application);
+ this.syncManager = new SyncManager(application);
+ }
+
+ public void saveCard(long accountId, long boardLocalId, long stackLocalId, @NonNull FullCard fullCard, @NonNull IResponseCallback<FullCard> callback) {
+ syncManager.createFullCard(accountId, boardLocalId, stackLocalId, fullCard, callback);
+ }
+
+ @SuppressWarnings("ConstantConditions")
+ public FullCard createFullCard(@NonNull Version version, @Nullable String subject, @Nullable String title, @Nullable String description) {
+ if (TextUtils.isEmpty(subject) && TextUtils.isEmpty(title) && TextUtils.isEmpty(description)) {
+ throw new IllegalArgumentException("Subject, title or description must not be empty.");
+ }
+ if (TextUtils.isEmpty(subject)) {
+ if (TextUtils.isEmpty(title)) {
+ return createFullCard(version, description);
+ } else if (TextUtils.isEmpty(description)) {
+ return createFullCard(version, title);
+ } else {
+ return createFullCard(version, title, description);
+ }
+ } else if (TextUtils.isEmpty(title)) {
+ if (TextUtils.isEmpty(subject)) {
+ return createFullCard(version, description);
+ } else if (TextUtils.isEmpty(description)) {
+ return createFullCard(version, subject);
+ } else {
+ return createFullCard(version, subject, description);
+ }
+ } else if (TextUtils.isEmpty(description)) {
+ if (TextUtils.isEmpty(subject)) {
+ return createFullCard(version, title);
+ } else if (TextUtils.isEmpty(title)) {
+ return createFullCard(version, description);
+ } else {
+ return createFullCard(version, subject, title);
+ }
+ } else {
+ return createFullCard(version, subject, title.trim() + "\n\n" + description.trim());
+ }
+ }
+
+ public FullCard createFullCard(@NonNull Version version, @NonNull String title, @NonNull String description) {
+ if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(description)) {
+ final FullCard fullCard = new FullCard();
+ final Card card = new Card();
+ final int maxLength = version.getCardTitleMaxLength();
+ if (title.length() > maxLength) {
+ card.setTitle(title.substring(0, maxLength));
+ card.setDescription(title.substring(maxLength).trim() + "\n\n" + description);
+ } else {
+ card.setTitle(title);
+ card.setDescription(description);
+ }
+ fullCard.setCard(card);
+ return fullCard;
+ } else if (!TextUtils.isEmpty(title)) {
+ return createFullCard(version, title);
+ } else if (!TextUtils.isEmpty(description)) {
+ return createFullCard(version, description);
+ } else {
+ throw new IllegalArgumentException("Title or description must not be empty.");
+ }
+ }
+
+ public FullCard createFullCard(@NonNull Version version, @NonNull String content) {
+ if (TextUtils.isEmpty(content)) {
+ throw new IllegalArgumentException("Content must not be empty.");
+ }
+ final FullCard fullCard = new FullCard();
+ final Card card = new Card();
+ final int maxLength = version.getCardTitleMaxLength();
+ if (content.length() > maxLength) {
+ card.setTitle(content.substring(0, maxLength).trim());
+ card.setDescription(content.substring(maxLength).trim());
+ } else {
+ card.setTitle(content);
+ card.setDescription(null);
+ }
+ fullCard.setCard(card);
+ return fullCard;
+ }
+}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/stack/StackWidgetConfigurationActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/stack/StackWidgetConfigurationActivity.java
index 07747b275..85c987740 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/stack/StackWidgetConfigurationActivity.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/stack/StackWidgetConfigurationActivity.java
@@ -1,9 +1,11 @@
package it.niedermann.nextcloud.deck.ui.widget.stack;
+import android.annotation.SuppressLint;
import android.appwidget.AppWidgetManager;
import android.content.Intent;
import android.os.Bundle;
+import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.lifecycle.ViewModelProvider;
@@ -11,6 +13,7 @@ import java.util.Collections;
import it.niedermann.nextcloud.deck.DeckLog;
import it.niedermann.nextcloud.deck.R;
+import it.niedermann.nextcloud.deck.api.IResponseCallback;
import it.niedermann.nextcloud.deck.api.ResponseCallback;
import it.niedermann.nextcloud.deck.model.Account;
import it.niedermann.nextcloud.deck.model.widget.filter.EWidgetType;
@@ -19,7 +22,6 @@ import it.niedermann.nextcloud.deck.model.widget.filter.FilterWidgetAccount;
import it.niedermann.nextcloud.deck.model.widget.filter.FilterWidgetBoard;
import it.niedermann.nextcloud.deck.model.widget.filter.FilterWidgetStack;
import it.niedermann.nextcloud.deck.ui.PickStackActivity;
-import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment;
public class StackWidgetConfigurationActivity extends PickStackActivity {
private int appWidgetId;
@@ -51,7 +53,7 @@ public class StackWidgetConfigurationActivity extends PickStackActivity {
}
@Override
- protected void onSubmit(Account account, long boardId, long stackId) {
+ protected void onSubmit(Account account, long boardId, long stackId, @NonNull IResponseCallback<Void> callback) {
final FilterWidget config = new FilterWidget(appWidgetId, EWidgetType.STACK_WIDGET);
final FilterWidgetAccount filterWidgetAccount = new FilterWidgetAccount(account.getId(), false);
filterWidgetAccount.setIncludeNoProject(false);
@@ -60,8 +62,8 @@ public class StackWidgetConfigurationActivity extends PickStackActivity {
filterWidgetAccount.setBoards(
Collections.singletonList(filterWidgetBoard));
config.setAccounts(Collections.singletonList(filterWidgetAccount));
- stackWidgetConfigurationViewModel.addStackWidget(config, new ResponseCallback<Integer>(account) {
+ stackWidgetConfigurationViewModel.addStackWidget(config, new ResponseCallback<Integer>(account) {
@Override
public void onResponse(Integer response) {
final Intent updateIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null,
@@ -69,16 +71,14 @@ public class StackWidgetConfigurationActivity extends PickStackActivity {
.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
setResult(RESULT_OK, updateIntent);
getApplicationContext().sendBroadcast(updateIntent);
-
+ callback.onResponse(null);
finish();
}
@Override
+ @SuppressLint("MissingSuperCall")
public void onError(Throwable throwable) {
- super.onError(throwable);
- ExceptionDialogFragment
- .newInstance(throwable, account)
- .show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ callback.onError(throwable);
}
});
}
diff --git a/app/src/main/res/layout/activity_pick_stack.xml b/app/src/main/res/layout/activity_pick_stack.xml
index 6ba9b567f..79cefb0f8 100644
--- a/app/src/main/res/layout/activity_pick_stack.xml
+++ b/app/src/main/res/layout/activity_pick_stack.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@@ -9,7 +9,11 @@
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ app:layout_constraintBottom_toTopOf="@id/inputWrapper"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
@@ -18,12 +22,47 @@
app:title="@string/add_card" />
</com.google.android.material.appbar.AppBarLayout>
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/inputWrapper"
+ style="@style/TextInputLayoutStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/title_mandatory"
+ android:labelFor="@id/input"
+ android:padding="@dimen/spacer_2x"
+ android:visibility="gone"
+ app:layout_constraintBottom_toTopOf="@id/fragment_container_wrapper"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/appBarLayout"
+ tools:visibility="visible">
+
+ <EditText
+ android:id="@+id/input"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:imeOptions="actionDone"
+ android:importantForAutofill="no"
+ android:inputType="text">
+
+ <requestFocus />
+ </EditText>
+
+ </com.google.android.material.textfield.TextInputLayout>
+
<ScrollView
+ android:id="@+id/fragment_container_wrapper"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="0dp"
android:layout_above="@+id/buttonBar"
android:layout_below="@id/appBarLayout"
- android:padding="@dimen/spacer_2x">
+ android:padding="@dimen/spacer_2x"
+ app:layout_constraintBottom_toTopOf="@id/buttonBar"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/inputWrapper"
+ app:layout_constraintVertical_weight="1"
+ tools:background="@color/bg_highlighted">
<FrameLayout
android:id="@+id/fragment_container"
@@ -38,7 +77,10 @@
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:padding="@dimen/spacer_2x"
- android:weightSum="1.0">
+ android:weightSum="1.0"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent">
<com.google.android.material.button.MaterialButton
android:id="@+id/cancel"
@@ -60,4 +102,4 @@
android:text="@string/simple_add"
app:backgroundTint="@color/defaultBrand" />
</LinearLayout>
-</RelativeLayout> \ No newline at end of file
+</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 972fd2c70..f9acf8b6b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -340,4 +340,7 @@
<string name="copying_logs_to_file">Copying logs to file…</string>
<string name="account_imported">Account imported</string>
<string name="simple_search">Search</string>
+ <!-- Label for a title input field that is required to be filled out -->
+ <string name="title_mandatory">Title (mandatory)</string>
+ <string name="saving_new_card">Saving new card…</string>
</resources>
diff --git a/app/src/test/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateViewModelTest.java b/app/src/test/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateViewModelTest.java
new file mode 100644
index 000000000..ef33099be
--- /dev/null
+++ b/app/src/test/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateViewModelTest.java
@@ -0,0 +1,151 @@
+package it.niedermann.nextcloud.deck.ui.preparecreate;
+
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import it.niedermann.nextcloud.deck.model.Card;
+import it.niedermann.nextcloud.deck.model.full.FullCard;
+import it.niedermann.nextcloud.deck.model.ocs.Version;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+@SuppressWarnings("ConstantConditions")
+@RunWith(RobolectricTestRunner.class)
+public class PrepareCreateViewModelTest {
+
+ @Rule
+ public InstantTaskExecutorRule instantTaskExecutorRule = new InstantTaskExecutorRule();
+
+ private PrepareCreateViewModel viewModel;
+
+ @Before
+ public void setup() {
+ viewModel = new PrepareCreateViewModel(ApplicationProvider.getApplicationContext());
+ }
+
+ @Test
+ public void createFullCardByContent() {
+ final Version v = Version.minimumSupported();
+ assertThrows(IllegalArgumentException.class, () -> viewModel.createFullCard(v, null));
+ assertThrows(IllegalArgumentException.class, () -> viewModel.createFullCard(v, ""));
+
+ Card card;
+
+ card = viewModel.createFullCard(v, "User entered text").getCard();
+ assertEquals("User entered text", card.getTitle());
+ assertNull(card.getDescription());
+
+ card = viewModel.createFullCard(v, "This is a very long content which will not fit into the card and should be split into title and desc ription. Whitespace should be trimmed.").getCard();
+ assertEquals("This is a very long content which will not fit into the card and should be split into title and desc", card.getTitle());
+ assertEquals("ription. Whitespace should be trimmed.", card.getDescription());
+ }
+
+ @Test
+ public void createFullCardByTitleAndDescription() {
+ final Version v = Version.minimumSupported();
+ assertThrows(IllegalArgumentException.class, () -> viewModel.createFullCard(v, null, null));
+ assertThrows(IllegalArgumentException.class, () -> viewModel.createFullCard(v, "", ""));
+ assertThrows(IllegalArgumentException.class, () -> viewModel.createFullCard(v, null, ""));
+ assertThrows(IllegalArgumentException.class, () -> viewModel.createFullCard(v, "", null));
+
+ Card card;
+
+ // content fits into title field
+
+ card = viewModel.createFullCard(v, "User entered text", null).getCard();
+ assertEquals("User entered text", card.getTitle());
+ assertNull(card.getDescription());
+
+ card = viewModel.createFullCard(v, null, "Fancy description").getCard();
+ assertEquals("Fancy description", card.getTitle());
+ assertNull(card.getDescription());
+
+ card = viewModel.createFullCard(v, "User entered text", "My description").getCard();
+ assertEquals("User entered text", card.getTitle());
+ assertEquals("My description", card.getDescription());
+
+ // content does not fit into title field
+
+ card = viewModel.createFullCard(v, "This is a very long content which will not fit into the card and should be split into title and desc ription. Whitespace should be trimmed.", null).getCard();
+ assertEquals("This is a very long content which will not fit into the card and should be split into title and desc", card.getTitle());
+ assertEquals("ription. Whitespace should be trimmed.", card.getDescription());
+
+ card = viewModel.createFullCard(v, null, "This is a very long content which will not fit into the card and should be split into title and desc ription. Whitespace should be trimmed.").getCard();
+ assertEquals("This is a very long content which will not fit into the card and should be split into title and desc", card.getTitle());
+ assertEquals("ription. Whitespace should be trimmed.", card.getDescription());
+
+ card = viewModel.createFullCard(v, "User entered text", "This is a very long content which will not fit into the card and should be split into title and description.").getCard();
+ assertEquals("User entered text", card.getTitle());
+ assertEquals("This is a very long content which will not fit into the card and should be split into title and description.", card.getDescription());
+
+ card = viewModel.createFullCard(v, "This is a very long content which will not fit into the card and should be split into title and description.", "My description").getCard();
+ assertEquals("This is a very long content which will not fit into the card and should be split into title and desc", card.getTitle());
+ assertEquals("ription.\n\nMy description", card.getDescription());
+
+ card = viewModel.createFullCard(v, "This is a very long content which will not fit into the card and should be split into title and description.", "This is a very long description which also will not fit into the card and should be split into title and description.").getCard();
+ assertEquals("This is a very long content which will not fit into the card and should be split into title and desc", card.getTitle());
+ assertEquals("ription.\n\nThis is a very long description which also will not fit into the card and should be split into title and description.", card.getDescription());
+ }
+
+ @Test
+ public void createFullCard() {
+ final PrepareCreateViewModel viewModel = spy(this.viewModel);
+ doReturn(new FullCard()).when(viewModel).createFullCard(any(), anyString());
+ doReturn(new FullCard()).when(viewModel).createFullCard(any(), anyString(), anyString());
+
+ final Version v = Version.minimumSupported();
+
+ assertThrows(IllegalArgumentException.class, () -> viewModel.createFullCard(v, null, null, null));
+ assertThrows(IllegalArgumentException.class, () -> viewModel.createFullCard(v, "", null, null));
+ assertThrows(IllegalArgumentException.class, () -> viewModel.createFullCard(v, null, "", null));
+ assertThrows(IllegalArgumentException.class, () -> viewModel.createFullCard(v, null, null, ""));
+ assertThrows(IllegalArgumentException.class, () -> viewModel.createFullCard(v, "", "", null));
+ assertThrows(IllegalArgumentException.class, () -> viewModel.createFullCard(v, "", null,""));
+ assertThrows(IllegalArgumentException.class, () -> viewModel.createFullCard(v, null, "",""));
+ assertThrows(IllegalArgumentException.class, () -> viewModel.createFullCard(v, "", "",""));
+
+ viewModel.createFullCard(v, "Foo", null, null);
+ verify(viewModel).createFullCard(any(), eq("Foo"));
+ reset(viewModel);
+
+ viewModel.createFullCard(v, null, "Bar", null);
+ verify(viewModel).createFullCard(any(), eq("Bar"));
+ reset(viewModel);
+
+ viewModel.createFullCard(v, null, null, "Baz");
+ verify(viewModel).createFullCard(any(), eq("Baz"));
+ reset(viewModel);
+
+ viewModel.createFullCard(v, "Foo", "Bar", null);
+ verify(viewModel).createFullCard(any(), eq("Foo"), eq("Bar"));
+ reset(viewModel);
+
+ viewModel.createFullCard(v, "Foo", null, "Baz");
+ verify(viewModel).createFullCard(any(), eq("Foo"), eq("Baz"));
+ reset(viewModel);
+
+ viewModel.createFullCard(v, null, "Bar", "Baz");
+ verify(viewModel).createFullCard(any(), eq("Bar"), eq("Baz"));
+ reset(viewModel);
+
+ viewModel.createFullCard(v, "Foo", "Bar", "Baz");
+ verify(viewModel).createFullCard(any(), eq("Foo"), eq("Bar\n\nBaz"));
+ reset(viewModel);
+ }
+}