mirror of https://github.com/tasks/tasks
List and account settings Kotlin conversions
parent
ae11547963
commit
320623045e
@ -1,190 +0,0 @@
|
|||||||
package org.tasks.activities;
|
|
||||||
|
|
||||||
import static org.tasks.dialogs.IconPickerDialog.newIconPicker;
|
|
||||||
import static org.tasks.themes.DrawableUtil.getLeftDrawable;
|
|
||||||
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.graphics.drawable.LayerDrawable;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import androidx.appcompat.widget.Toolbar;
|
|
||||||
import androidx.appcompat.widget.Toolbar.OnMenuItemClickListener;
|
|
||||||
import butterknife.BindView;
|
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import butterknife.OnClick;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.tasks.R;
|
|
||||||
import org.tasks.dialogs.ColorPalettePicker;
|
|
||||||
import org.tasks.dialogs.ColorPickerAdapter.Palette;
|
|
||||||
import org.tasks.dialogs.ColorWheelPicker;
|
|
||||||
import org.tasks.dialogs.DialogBuilder;
|
|
||||||
import org.tasks.dialogs.IconPickerDialog.IconPickerCallback;
|
|
||||||
import org.tasks.injection.ThemedInjectingAppCompatActivity;
|
|
||||||
import org.tasks.themes.ColorProvider;
|
|
||||||
import org.tasks.themes.CustomIcons;
|
|
||||||
import org.tasks.themes.DrawableUtil;
|
|
||||||
import org.tasks.themes.ThemeColor;
|
|
||||||
|
|
||||||
public abstract class BaseListSettingsActivity extends ThemedInjectingAppCompatActivity
|
|
||||||
implements IconPickerCallback,
|
|
||||||
OnMenuItemClickListener,
|
|
||||||
ColorPalettePicker.ColorPickedCallback,
|
|
||||||
ColorWheelPicker.ColorPickedCallback {
|
|
||||||
|
|
||||||
private static final String EXTRA_SELECTED_THEME = "extra_selected_theme";
|
|
||||||
private static final String EXTRA_SELECTED_ICON = "extra_selected_icon";
|
|
||||||
private static final String FRAG_TAG_ICON_PICKER = "frag_tag_icon_picker";
|
|
||||||
private static final String FRAG_TAG_COLOR_PICKER = "frag_tag_color_picker";
|
|
||||||
protected int selectedColor = 0;
|
|
||||||
protected int selectedIcon = -1;
|
|
||||||
|
|
||||||
@BindView(R.id.clear)
|
|
||||||
View clear;
|
|
||||||
|
|
||||||
@BindView(R.id.color)
|
|
||||||
TextView color;
|
|
||||||
|
|
||||||
@BindView(R.id.icon)
|
|
||||||
TextView icon;
|
|
||||||
|
|
||||||
@BindView(R.id.toolbar)
|
|
||||||
protected Toolbar toolbar;
|
|
||||||
|
|
||||||
@Inject DialogBuilder dialogBuilder;
|
|
||||||
@Inject ColorProvider colorProvider;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
setContentView(getLayout());
|
|
||||||
|
|
||||||
ButterKnife.bind(this);
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
selectedColor = savedInstanceState.getInt(EXTRA_SELECTED_THEME);
|
|
||||||
selectedIcon = savedInstanceState.getInt(EXTRA_SELECTED_ICON);
|
|
||||||
}
|
|
||||||
|
|
||||||
toolbar.setTitle(getToolbarTitle());
|
|
||||||
toolbar.setNavigationIcon(getDrawable(R.drawable.ic_outline_save_24px));
|
|
||||||
toolbar.setNavigationOnClickListener(v -> save());
|
|
||||||
if (!isNew()) {
|
|
||||||
toolbar.inflateMenu(R.menu.menu_tag_settings);
|
|
||||||
}
|
|
||||||
toolbar.setOnMenuItemClickListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onSaveInstanceState(Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
|
|
||||||
outState.putInt(EXTRA_SELECTED_THEME, selectedColor);
|
|
||||||
outState.putInt(EXTRA_SELECTED_ICON, selectedIcon);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
discard();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract int getLayout();
|
|
||||||
|
|
||||||
protected abstract boolean hasChanges();
|
|
||||||
|
|
||||||
protected abstract void save();
|
|
||||||
|
|
||||||
protected abstract boolean isNew();
|
|
||||||
|
|
||||||
protected abstract String getToolbarTitle();
|
|
||||||
|
|
||||||
protected abstract void delete();
|
|
||||||
|
|
||||||
protected void discard() {
|
|
||||||
if (!hasChanges()) {
|
|
||||||
finish();
|
|
||||||
} else {
|
|
||||||
dialogBuilder
|
|
||||||
.newDialog(R.string.discard_changes)
|
|
||||||
.setPositiveButton(R.string.discard, (dialog, which) -> finish())
|
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnClick(R.id.clear)
|
|
||||||
protected void clearColor() {
|
|
||||||
onColorPicked(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnClick(R.id.color_row)
|
|
||||||
protected void showThemePicker() {
|
|
||||||
ColorPalettePicker.Companion.newColorPalette(null, 0, selectedColor, Palette.COLORS)
|
|
||||||
.show(getSupportFragmentManager(), FRAG_TAG_COLOR_PICKER);
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnClick(R.id.icon_row)
|
|
||||||
protected void showIconPicker() {
|
|
||||||
newIconPicker(selectedIcon).show(getSupportFragmentManager(), FRAG_TAG_ICON_PICKER);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSelected(DialogInterface dialogInterface, int icon) {
|
|
||||||
this.selectedIcon = icon;
|
|
||||||
dialogInterface.dismiss();
|
|
||||||
updateTheme();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onColorPicked(int color) {
|
|
||||||
selectedColor = color;
|
|
||||||
updateTheme();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onMenuItemClick(MenuItem item) {
|
|
||||||
if (item.getItemId() == R.id.delete) {
|
|
||||||
promptDelete();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void promptDelete() {
|
|
||||||
dialogBuilder
|
|
||||||
.newDialog(R.string.delete_tag_confirmation, getToolbarTitle())
|
|
||||||
.setPositiveButton(R.string.delete, (dialog, which) -> delete())
|
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void updateTheme() {
|
|
||||||
ThemeColor themeColor;
|
|
||||||
if (selectedColor == 0) {
|
|
||||||
themeColor = this.themeColor;
|
|
||||||
DrawableUtil.setLeftDrawable(this, color, R.drawable.ic_outline_not_interested_24px);
|
|
||||||
getLeftDrawable(color).setTint(getColor(R.color.icon_tint_with_alpha));
|
|
||||||
clear.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
themeColor = colorProvider.getThemeColor(selectedColor, true);
|
|
||||||
DrawableUtil.setLeftDrawable(this, color, R.drawable.color_picker);
|
|
||||||
Drawable leftDrawable = getLeftDrawable(color);
|
|
||||||
(leftDrawable instanceof LayerDrawable
|
|
||||||
? ((LayerDrawable) leftDrawable).getDrawable(0)
|
|
||||||
: leftDrawable)
|
|
||||||
.setTint(themeColor.getPrimaryColor());
|
|
||||||
clear.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
themeColor.apply(toolbar);
|
|
||||||
themeColor.applyToSystemBars(this);
|
|
||||||
Integer icon = CustomIcons.getIconResId(selectedIcon);
|
|
||||||
if (icon == null) {
|
|
||||||
icon = CustomIcons.getIconResId(CustomIcons.LIST);
|
|
||||||
}
|
|
||||||
DrawableUtil.setLeftDrawable(this, this.icon, icon);
|
|
||||||
getLeftDrawable(this.icon).setTint(getColor(R.color.icon_tint_with_alpha));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,166 @@
|
|||||||
|
package org.tasks.activities
|
||||||
|
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.graphics.drawable.LayerDrawable
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.widget.Toolbar
|
||||||
|
import butterknife.BindView
|
||||||
|
import butterknife.ButterKnife
|
||||||
|
import butterknife.OnClick
|
||||||
|
import org.tasks.R
|
||||||
|
import org.tasks.dialogs.ColorPalettePicker
|
||||||
|
import org.tasks.dialogs.ColorPalettePicker.Companion.newColorPalette
|
||||||
|
import org.tasks.dialogs.ColorPickerAdapter.Palette
|
||||||
|
import org.tasks.dialogs.ColorWheelPicker
|
||||||
|
import org.tasks.dialogs.DialogBuilder
|
||||||
|
import org.tasks.dialogs.IconPickerDialog
|
||||||
|
import org.tasks.dialogs.IconPickerDialog.IconPickerCallback
|
||||||
|
import org.tasks.injection.ThemedInjectingAppCompatActivity
|
||||||
|
import org.tasks.themes.ColorProvider
|
||||||
|
import org.tasks.themes.CustomIcons
|
||||||
|
import org.tasks.themes.CustomIcons.getIconResId
|
||||||
|
import org.tasks.themes.DrawableUtil
|
||||||
|
import org.tasks.themes.ThemeColor
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
abstract class BaseListSettingsActivity : ThemedInjectingAppCompatActivity(), IconPickerCallback, Toolbar.OnMenuItemClickListener, ColorPalettePicker.ColorPickedCallback, ColorWheelPicker.ColorPickedCallback {
|
||||||
|
@Inject lateinit var dialogBuilder: DialogBuilder
|
||||||
|
@Inject lateinit var colorProvider: ColorProvider
|
||||||
|
protected var selectedColor = 0
|
||||||
|
protected var selectedIcon = -1
|
||||||
|
|
||||||
|
@BindView(R.id.clear)
|
||||||
|
lateinit var clear: View
|
||||||
|
|
||||||
|
@BindView(R.id.color)
|
||||||
|
lateinit var color: TextView
|
||||||
|
|
||||||
|
@BindView(R.id.icon)
|
||||||
|
lateinit var icon: TextView
|
||||||
|
|
||||||
|
@BindView(R.id.toolbar)
|
||||||
|
lateinit var toolbar: Toolbar
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(layout)
|
||||||
|
ButterKnife.bind(this)
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
selectedColor = savedInstanceState.getInt(EXTRA_SELECTED_THEME)
|
||||||
|
selectedIcon = savedInstanceState.getInt(EXTRA_SELECTED_ICON)
|
||||||
|
}
|
||||||
|
toolbar.title = toolbarTitle
|
||||||
|
toolbar.navigationIcon = getDrawable(R.drawable.ic_outline_save_24px)
|
||||||
|
toolbar.setNavigationOnClickListener { save() }
|
||||||
|
if (!isNew) {
|
||||||
|
toolbar.inflateMenu(R.menu.menu_tag_settings)
|
||||||
|
}
|
||||||
|
toolbar.setOnMenuItemClickListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
outState.putInt(EXTRA_SELECTED_THEME, selectedColor)
|
||||||
|
outState.putInt(EXTRA_SELECTED_ICON, selectedIcon)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
discard()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract val layout: Int
|
||||||
|
protected abstract fun hasChanges(): Boolean
|
||||||
|
protected abstract fun save()
|
||||||
|
protected abstract val isNew: Boolean
|
||||||
|
protected abstract val toolbarTitle: String?
|
||||||
|
protected abstract fun delete()
|
||||||
|
protected open fun discard() {
|
||||||
|
if (!hasChanges()) {
|
||||||
|
finish()
|
||||||
|
} else {
|
||||||
|
dialogBuilder
|
||||||
|
.newDialog(R.string.discard_changes)
|
||||||
|
.setPositiveButton(R.string.discard) { _, _ -> finish() }
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick(R.id.clear)
|
||||||
|
fun clearColor() {
|
||||||
|
onColorPicked(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick(R.id.color_row)
|
||||||
|
fun showThemePicker() {
|
||||||
|
newColorPalette(null, 0, selectedColor, Palette.COLORS)
|
||||||
|
.show(supportFragmentManager, FRAG_TAG_COLOR_PICKER)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick(R.id.icon_row)
|
||||||
|
fun showIconPicker() {
|
||||||
|
IconPickerDialog.newIconPicker(selectedIcon).show(supportFragmentManager, FRAG_TAG_ICON_PICKER)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSelected(dialogInterface: DialogInterface, icon: Int) {
|
||||||
|
selectedIcon = icon
|
||||||
|
dialogInterface.dismiss()
|
||||||
|
updateTheme()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onColorPicked(color: Int) {
|
||||||
|
selectedColor = color
|
||||||
|
updateTheme()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||||
|
if (item.itemId == R.id.delete) {
|
||||||
|
promptDelete()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun promptDelete() {
|
||||||
|
dialogBuilder
|
||||||
|
.newDialog(R.string.delete_tag_confirmation, toolbarTitle)
|
||||||
|
.setPositiveButton(R.string.delete) { _, _ -> delete() }
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun updateTheme() {
|
||||||
|
val themeColor: ThemeColor
|
||||||
|
if (selectedColor == 0) {
|
||||||
|
themeColor = this.themeColor
|
||||||
|
DrawableUtil.setLeftDrawable(this, color, R.drawable.ic_outline_not_interested_24px)
|
||||||
|
DrawableUtil.getLeftDrawable(color).setTint(getColor(R.color.icon_tint_with_alpha))
|
||||||
|
clear.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
themeColor = colorProvider.getThemeColor(selectedColor, true)
|
||||||
|
DrawableUtil.setLeftDrawable(this, color, R.drawable.color_picker)
|
||||||
|
val leftDrawable = DrawableUtil.getLeftDrawable(color)
|
||||||
|
(if (leftDrawable is LayerDrawable) leftDrawable.getDrawable(0) else leftDrawable)
|
||||||
|
.setTint(themeColor.primaryColor)
|
||||||
|
clear.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
themeColor.apply(toolbar)
|
||||||
|
themeColor.applyToSystemBars(this)
|
||||||
|
var icon = getIconResId(selectedIcon)
|
||||||
|
if (icon == null) {
|
||||||
|
icon = getIconResId(CustomIcons.LIST)
|
||||||
|
}
|
||||||
|
DrawableUtil.setLeftDrawable(this, this.icon, icon!!)
|
||||||
|
DrawableUtil.getLeftDrawable(this.icon).setTint(getColor(R.color.icon_tint_with_alpha))
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val EXTRA_SELECTED_THEME = "extra_selected_theme"
|
||||||
|
private const val EXTRA_SELECTED_ICON = "extra_selected_icon"
|
||||||
|
private const val FRAG_TAG_ICON_PICKER = "frag_tag_icon_picker"
|
||||||
|
private const val FRAG_TAG_COLOR_PICKER = "frag_tag_color_picker"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,13 @@
|
|||||||
package org.tasks.activities
|
package org.tasks.activities
|
||||||
|
|
||||||
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
import com.google.api.services.tasks.model.TaskList
|
import com.google.api.services.tasks.model.TaskList
|
||||||
import com.todoroo.astrid.gtasks.api.GtasksInvoker
|
import com.todoroo.astrid.gtasks.api.GtasksInvoker
|
||||||
import org.tasks.ui.CompletableViewModel
|
import org.tasks.ui.CompletableViewModel
|
||||||
|
|
||||||
class CreateListViewModel : CompletableViewModel<TaskList?>() {
|
class CreateListViewModel @ViewModelInject constructor(
|
||||||
fun createList(invoker: GtasksInvoker, account: String?, name: String?) {
|
private val invoker: GtasksInvoker) : CompletableViewModel<TaskList>() {
|
||||||
run { invoker.forAccount(account!!).createGtaskList(name) }
|
fun createList(account: String, name: String) {
|
||||||
|
run { invoker.forAccount(account).createGtaskList(name)!! }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,12 +0,0 @@
|
|||||||
package org.tasks.activities;
|
|
||||||
|
|
||||||
import com.todoroo.astrid.gtasks.api.GtasksInvoker;
|
|
||||||
import org.tasks.data.GoogleTaskList;
|
|
||||||
import org.tasks.ui.ActionViewModel;
|
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
|
||||||
public class DeleteListViewModel extends ActionViewModel {
|
|
||||||
void deleteList(GtasksInvoker invoker, GoogleTaskList list) {
|
|
||||||
run(() -> invoker.forAccount(list.getAccount()).deleteGtaskList(list.getRemoteId()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package org.tasks.activities
|
||||||
|
|
||||||
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
|
import com.todoroo.astrid.gtasks.api.GtasksInvoker
|
||||||
|
import org.tasks.data.GoogleTaskList
|
||||||
|
import org.tasks.ui.ActionViewModel
|
||||||
|
|
||||||
|
class DeleteListViewModel @ViewModelInject constructor(
|
||||||
|
private val invoker: GtasksInvoker) : ActionViewModel() {
|
||||||
|
fun deleteList(list: GoogleTaskList) {
|
||||||
|
run { invoker.forAccount(list.account!!).deleteGtaskList(list.remoteId) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,249 +0,0 @@
|
|||||||
package org.tasks.activities;
|
|
||||||
|
|
||||||
import static org.tasks.Strings.isNullOrEmpty;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.inputmethod.InputMethodManager;
|
|
||||||
import android.widget.ProgressBar;
|
|
||||||
import android.widget.Toast;
|
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
|
||||||
import butterknife.BindView;
|
|
||||||
import com.google.android.material.textfield.TextInputEditText;
|
|
||||||
import com.google.api.services.tasks.model.TaskList;
|
|
||||||
import com.todoroo.astrid.activity.MainActivity;
|
|
||||||
import com.todoroo.astrid.activity.TaskListFragment;
|
|
||||||
import com.todoroo.astrid.api.GtasksFilter;
|
|
||||||
import com.todoroo.astrid.gtasks.GtasksListService;
|
|
||||||
import com.todoroo.astrid.gtasks.api.GtasksInvoker;
|
|
||||||
import com.todoroo.astrid.service.TaskDeleter;
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint;
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import kotlin.Unit;
|
|
||||||
import org.tasks.R;
|
|
||||||
import org.tasks.data.GoogleTaskAccount;
|
|
||||||
import org.tasks.data.GoogleTaskList;
|
|
||||||
import org.tasks.data.GoogleTaskListDaoBlocking;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
public class GoogleTaskListSettingsActivity extends BaseListSettingsActivity {
|
|
||||||
|
|
||||||
public static final String EXTRA_ACCOUNT = "extra_account";
|
|
||||||
public static final String EXTRA_STORE_DATA = "extra_store_data";
|
|
||||||
@Inject @ApplicationContext Context context;
|
|
||||||
@Inject GoogleTaskListDaoBlocking googleTaskListDao;
|
|
||||||
@Inject GtasksListService gtasksListService;
|
|
||||||
@Inject TaskDeleter taskDeleter;
|
|
||||||
@Inject GtasksInvoker gtasksInvoker;
|
|
||||||
|
|
||||||
@BindView(R.id.name)
|
|
||||||
TextInputEditText name;
|
|
||||||
|
|
||||||
@BindView(R.id.progress_bar)
|
|
||||||
ProgressBar progressView;
|
|
||||||
|
|
||||||
private boolean isNewList;
|
|
||||||
private GoogleTaskList gtasksList;
|
|
||||||
private CreateListViewModel createListViewModel;
|
|
||||||
private RenameListViewModel renameListViewModel;
|
|
||||||
private DeleteListViewModel deleteListViewModel;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
Intent intent = getIntent();
|
|
||||||
gtasksList = intent.getParcelableExtra(EXTRA_STORE_DATA);
|
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
ViewModelProvider provider = new ViewModelProvider(this);
|
|
||||||
createListViewModel = provider.get(CreateListViewModel.class);
|
|
||||||
renameListViewModel = provider.get(RenameListViewModel.class);
|
|
||||||
deleteListViewModel = provider.get(DeleteListViewModel.class);
|
|
||||||
|
|
||||||
if (gtasksList == null) {
|
|
||||||
isNewList = true;
|
|
||||||
gtasksList = new GoogleTaskList();
|
|
||||||
GoogleTaskAccount account = intent.getParcelableExtra(EXTRA_ACCOUNT);
|
|
||||||
gtasksList.setAccount(account.getAccount());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
|
||||||
selectedColor = gtasksList.getColor();
|
|
||||||
selectedIcon = gtasksList.getIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNewList) {
|
|
||||||
name.requestFocus();
|
|
||||||
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
||||||
imm.showSoftInput(name, InputMethodManager.SHOW_IMPLICIT);
|
|
||||||
} else {
|
|
||||||
name.setText(gtasksList.getTitle());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (createListViewModel.inProgress()
|
|
||||||
|| renameListViewModel.inProgress()
|
|
||||||
|| deleteListViewModel.inProgress()) {
|
|
||||||
showProgressIndicator();
|
|
||||||
}
|
|
||||||
createListViewModel.observe(this, this::onListCreated, this::requestFailed);
|
|
||||||
renameListViewModel.observe(this, this::onListRenamed, this::requestFailed);
|
|
||||||
deleteListViewModel.observe(this, this::onListDeleted, this::requestFailed);
|
|
||||||
|
|
||||||
updateTheme();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isNew() {
|
|
||||||
return gtasksList == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getToolbarTitle() {
|
|
||||||
return isNew() ? getString(R.string.new_list) : gtasksList.getTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showProgressIndicator() {
|
|
||||||
progressView.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void hideProgressIndicator() {
|
|
||||||
progressView.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean requestInProgress() {
|
|
||||||
return progressView.getVisibility() == View.VISIBLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void save() {
|
|
||||||
if (requestInProgress()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String newName = getNewName();
|
|
||||||
|
|
||||||
if (isNullOrEmpty(newName)) {
|
|
||||||
Toast.makeText(this, R.string.name_cannot_be_empty, Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNewList) {
|
|
||||||
showProgressIndicator();
|
|
||||||
createListViewModel.createList(gtasksInvoker, gtasksList.getAccount(), newName);
|
|
||||||
} else if (nameChanged()) {
|
|
||||||
showProgressIndicator();
|
|
||||||
renameListViewModel.renameList(gtasksInvoker, gtasksList, newName);
|
|
||||||
} else {
|
|
||||||
if (colorChanged() || iconChanged()) {
|
|
||||||
gtasksList.setColor(selectedColor);
|
|
||||||
gtasksList.setIcon(selectedIcon);
|
|
||||||
googleTaskListDao.insertOrReplace(gtasksList);
|
|
||||||
setResult(
|
|
||||||
RESULT_OK,
|
|
||||||
new Intent(TaskListFragment.ACTION_RELOAD)
|
|
||||||
.putExtra(MainActivity.OPEN_FILTER, new GtasksFilter(gtasksList)));
|
|
||||||
}
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void finish() {
|
|
||||||
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
||||||
imm.hideSoftInputFromWindow(name.getWindowToken(), 0);
|
|
||||||
super.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int getLayout() {
|
|
||||||
return R.layout.activity_google_task_list_settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void promptDelete() {
|
|
||||||
if (!requestInProgress()) {
|
|
||||||
super.promptDelete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void delete() {
|
|
||||||
showProgressIndicator();
|
|
||||||
deleteListViewModel.deleteList(gtasksInvoker, gtasksList);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void discard() {
|
|
||||||
if (!requestInProgress()) {
|
|
||||||
super.discard();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getNewName() {
|
|
||||||
return name.getText().toString().trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean hasChanges() {
|
|
||||||
if (isNewList) {
|
|
||||||
return selectedColor >= 0 || !isNullOrEmpty(getNewName());
|
|
||||||
}
|
|
||||||
return colorChanged() || nameChanged() || iconChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean colorChanged() {
|
|
||||||
return selectedColor != gtasksList.getColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean iconChanged() {
|
|
||||||
return selectedIcon != gtasksList.getIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean nameChanged() {
|
|
||||||
return !getNewName().equals(gtasksList.getTitle());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Unit onListCreated(TaskList taskList) {
|
|
||||||
gtasksList.setRemoteId(taskList.getId());
|
|
||||||
gtasksList.setTitle(taskList.getTitle());
|
|
||||||
gtasksList.setColor(selectedColor);
|
|
||||||
gtasksList.setIcon(selectedIcon);
|
|
||||||
gtasksList.setId(googleTaskListDao.insertOrReplace(gtasksList));
|
|
||||||
setResult(
|
|
||||||
RESULT_OK, new Intent().putExtra(MainActivity.OPEN_FILTER, new GtasksFilter(gtasksList)));
|
|
||||||
finish();
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onListDeleted(boolean deleted) {
|
|
||||||
if (deleted) {
|
|
||||||
taskDeleter.delete(gtasksList);
|
|
||||||
setResult(RESULT_OK, new Intent(TaskListFragment.ACTION_DELETED));
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Unit onListRenamed(TaskList taskList) {
|
|
||||||
gtasksList.setTitle(taskList.getTitle());
|
|
||||||
gtasksList.setColor(selectedColor);
|
|
||||||
gtasksList.setIcon(selectedIcon);
|
|
||||||
googleTaskListDao.insertOrReplace(gtasksList);
|
|
||||||
setResult(
|
|
||||||
RESULT_OK,
|
|
||||||
new Intent(TaskListFragment.ACTION_RELOAD)
|
|
||||||
.putExtra(MainActivity.OPEN_FILTER, new GtasksFilter(gtasksList)));
|
|
||||||
finish();
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Unit requestFailed(Throwable error) {
|
|
||||||
Timber.e(error);
|
|
||||||
hideProgressIndicator();
|
|
||||||
Toast.makeText(this, R.string.gtasks_GLA_errorIOAuth, Toast.LENGTH_LONG).show();
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,214 @@
|
|||||||
|
package org.tasks.activities
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.ProgressBar
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import butterknife.BindView
|
||||||
|
import com.google.android.material.textfield.TextInputEditText
|
||||||
|
import com.google.api.services.tasks.model.TaskList
|
||||||
|
import com.todoroo.astrid.activity.MainActivity
|
||||||
|
import com.todoroo.astrid.activity.TaskListFragment
|
||||||
|
import com.todoroo.astrid.api.GtasksFilter
|
||||||
|
import com.todoroo.astrid.gtasks.GtasksListService
|
||||||
|
import com.todoroo.astrid.gtasks.api.GtasksInvoker
|
||||||
|
import com.todoroo.astrid.service.TaskDeleter
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import org.tasks.R
|
||||||
|
import org.tasks.Strings.isNullOrEmpty
|
||||||
|
import org.tasks.data.GoogleTaskAccount
|
||||||
|
import org.tasks.data.GoogleTaskList
|
||||||
|
import org.tasks.data.GoogleTaskListDaoBlocking
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class GoogleTaskListSettingsActivity : BaseListSettingsActivity() {
|
||||||
|
@Inject @ApplicationContext lateinit var context: Context
|
||||||
|
@Inject lateinit var googleTaskListDao: GoogleTaskListDaoBlocking
|
||||||
|
@Inject lateinit var gtasksListService: GtasksListService
|
||||||
|
@Inject lateinit var taskDeleter: TaskDeleter
|
||||||
|
@Inject lateinit var gtasksInvoker: GtasksInvoker
|
||||||
|
|
||||||
|
@BindView(R.id.name)
|
||||||
|
lateinit var name: TextInputEditText
|
||||||
|
|
||||||
|
@BindView(R.id.progress_bar)
|
||||||
|
lateinit var progressView: ProgressBar
|
||||||
|
|
||||||
|
private var isNewList = false
|
||||||
|
private lateinit var gtasksList: GoogleTaskList
|
||||||
|
private val createListViewModel: CreateListViewModel by viewModels()
|
||||||
|
private val renameListViewModel: RenameListViewModel by viewModels()
|
||||||
|
private val deleteListViewModel: DeleteListViewModel by viewModels()
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
gtasksList = intent.getParcelableExtra(EXTRA_STORE_DATA)
|
||||||
|
?: GoogleTaskList().apply {
|
||||||
|
isNewList = true
|
||||||
|
account = intent.getParcelableExtra<GoogleTaskAccount>(EXTRA_ACCOUNT)!!.account
|
||||||
|
}
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
selectedColor = gtasksList.getColor()!!
|
||||||
|
selectedIcon = gtasksList.getIcon()!!
|
||||||
|
}
|
||||||
|
if (isNewList) {
|
||||||
|
name.requestFocus()
|
||||||
|
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
imm.showSoftInput(name, InputMethodManager.SHOW_IMPLICIT)
|
||||||
|
} else {
|
||||||
|
name.setText(gtasksList.title)
|
||||||
|
}
|
||||||
|
if (createListViewModel.inProgress
|
||||||
|
|| renameListViewModel.inProgress
|
||||||
|
|| deleteListViewModel.inProgress) {
|
||||||
|
showProgressIndicator()
|
||||||
|
}
|
||||||
|
createListViewModel.observe(this, this::onListCreated, this::requestFailed)
|
||||||
|
renameListViewModel.observe(this, this::onListRenamed, this::requestFailed)
|
||||||
|
deleteListViewModel.observe(this, this::onListDeleted, this::requestFailed)
|
||||||
|
updateTheme()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val isNew: Boolean
|
||||||
|
get() = isNewList
|
||||||
|
|
||||||
|
override val toolbarTitle: String?
|
||||||
|
get() = if (isNew) getString(R.string.new_list) else gtasksList.title!!
|
||||||
|
|
||||||
|
private fun showProgressIndicator() {
|
||||||
|
progressView.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideProgressIndicator() {
|
||||||
|
progressView.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun requestInProgress() = progressView.visibility == View.VISIBLE
|
||||||
|
|
||||||
|
override fun save() {
|
||||||
|
if (requestInProgress()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val newName = newName
|
||||||
|
if (isNullOrEmpty(newName)) {
|
||||||
|
Toast.makeText(this, R.string.name_cannot_be_empty, Toast.LENGTH_LONG).show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
when {
|
||||||
|
isNewList -> {
|
||||||
|
showProgressIndicator()
|
||||||
|
createListViewModel.createList(gtasksList.account!!, newName)
|
||||||
|
}
|
||||||
|
nameChanged() -> {
|
||||||
|
showProgressIndicator()
|
||||||
|
renameListViewModel.renameList(gtasksList, newName)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
if (colorChanged() || iconChanged()) {
|
||||||
|
gtasksList.setColor(selectedColor)
|
||||||
|
gtasksList.setIcon(selectedIcon)
|
||||||
|
googleTaskListDao.insertOrReplace(gtasksList)
|
||||||
|
setResult(
|
||||||
|
Activity.RESULT_OK,
|
||||||
|
Intent(TaskListFragment.ACTION_RELOAD)
|
||||||
|
.putExtra(MainActivity.OPEN_FILTER, GtasksFilter(gtasksList)))
|
||||||
|
}
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finish() {
|
||||||
|
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
imm.hideSoftInputFromWindow(name.windowToken, 0)
|
||||||
|
super.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val layout: Int
|
||||||
|
get() = R.layout.activity_google_task_list_settings
|
||||||
|
|
||||||
|
override fun promptDelete() {
|
||||||
|
if (!requestInProgress()) {
|
||||||
|
super.promptDelete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun delete() {
|
||||||
|
showProgressIndicator()
|
||||||
|
deleteListViewModel.deleteList(gtasksList)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun discard() {
|
||||||
|
if (!requestInProgress()) {
|
||||||
|
super.discard()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val newName: String
|
||||||
|
get() = name.text.toString().trim { it <= ' ' }
|
||||||
|
|
||||||
|
override fun hasChanges(): Boolean {
|
||||||
|
return if (isNewList) {
|
||||||
|
selectedColor >= 0 || !isNullOrEmpty(newName)
|
||||||
|
} else colorChanged() || nameChanged() || iconChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun colorChanged() = selectedColor != gtasksList.getColor()
|
||||||
|
|
||||||
|
private fun iconChanged() = selectedIcon != gtasksList.getIcon()
|
||||||
|
|
||||||
|
private fun nameChanged() = newName != gtasksList.title
|
||||||
|
|
||||||
|
private fun onListCreated(taskList: TaskList) {
|
||||||
|
gtasksList.remoteId = taskList.id
|
||||||
|
gtasksList.title = taskList.title
|
||||||
|
gtasksList.setColor(selectedColor)
|
||||||
|
gtasksList.setIcon(selectedIcon)
|
||||||
|
gtasksList.id = googleTaskListDao.insertOrReplace(gtasksList)
|
||||||
|
setResult(
|
||||||
|
Activity.RESULT_OK, Intent().putExtra(MainActivity.OPEN_FILTER, GtasksFilter(gtasksList)))
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onListDeleted(deleted: Boolean) {
|
||||||
|
if (deleted) {
|
||||||
|
taskDeleter.delete(gtasksList)
|
||||||
|
setResult(Activity.RESULT_OK, Intent(TaskListFragment.ACTION_DELETED))
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onListRenamed(taskList: TaskList) {
|
||||||
|
gtasksList.title = taskList.title
|
||||||
|
gtasksList.setColor(selectedColor)
|
||||||
|
gtasksList.setIcon(selectedIcon)
|
||||||
|
googleTaskListDao.insertOrReplace(gtasksList)
|
||||||
|
setResult(
|
||||||
|
Activity.RESULT_OK,
|
||||||
|
Intent(TaskListFragment.ACTION_RELOAD)
|
||||||
|
.putExtra(MainActivity.OPEN_FILTER, GtasksFilter(gtasksList)))
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun requestFailed(error: Throwable) {
|
||||||
|
Timber.e(error)
|
||||||
|
hideProgressIndicator()
|
||||||
|
Toast.makeText(this, R.string.gtasks_GLA_errorIOAuth, Toast.LENGTH_LONG).show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val EXTRA_ACCOUNT = "extra_account"
|
||||||
|
const val EXTRA_STORE_DATA = "extra_store_data"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +1,14 @@
|
|||||||
package org.tasks.activities
|
package org.tasks.activities
|
||||||
|
|
||||||
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
import com.google.api.services.tasks.model.TaskList
|
import com.google.api.services.tasks.model.TaskList
|
||||||
import com.todoroo.astrid.gtasks.api.GtasksInvoker
|
import com.todoroo.astrid.gtasks.api.GtasksInvoker
|
||||||
import org.tasks.data.GoogleTaskList
|
import org.tasks.data.GoogleTaskList
|
||||||
import org.tasks.ui.CompletableViewModel
|
import org.tasks.ui.CompletableViewModel
|
||||||
|
|
||||||
class RenameListViewModel : CompletableViewModel<TaskList?>() {
|
class RenameListViewModel @ViewModelInject constructor(
|
||||||
fun renameList(invoker: GtasksInvoker, list: GoogleTaskList, name: String?) {
|
private val invoker: GtasksInvoker) : CompletableViewModel<TaskList>() {
|
||||||
run { invoker.forAccount(list.account!!).renameGtaskList(list.remoteId, name) }
|
fun renameList(list: GoogleTaskList, name: String) {
|
||||||
|
run { invoker.forAccount(list.account!!).renameGtaskList(list.remoteId, name)!! }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,171 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.tasks.activities;
|
|
||||||
|
|
||||||
import static org.tasks.Strings.isNullOrEmpty;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.inputmethod.InputMethodManager;
|
|
||||||
import butterknife.BindView;
|
|
||||||
import butterknife.OnTextChanged;
|
|
||||||
import com.google.android.material.textfield.TextInputEditText;
|
|
||||||
import com.google.android.material.textfield.TextInputLayout;
|
|
||||||
import com.todoroo.astrid.activity.MainActivity;
|
|
||||||
import com.todoroo.astrid.activity.TaskListFragment;
|
|
||||||
import com.todoroo.astrid.api.TagFilter;
|
|
||||||
import com.todoroo.astrid.helper.UUIDHelper;
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.tasks.R;
|
|
||||||
import org.tasks.data.TagDaoBlocking;
|
|
||||||
import org.tasks.data.TagData;
|
|
||||||
import org.tasks.data.TagDataDaoBlocking;
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
public class TagSettingsActivity extends BaseListSettingsActivity {
|
|
||||||
|
|
||||||
public static final String TOKEN_AUTOPOPULATE_NAME = "autopopulateName"; // $NON-NLS-1$
|
|
||||||
public static final String EXTRA_TAG_DATA = "tagData"; // $NON-NLS-1$
|
|
||||||
private static final String EXTRA_TAG_UUID = "uuid"; // $NON-NLS-1$
|
|
||||||
@Inject TagDataDaoBlocking tagDataDao;
|
|
||||||
@Inject TagDaoBlocking tagDao;
|
|
||||||
|
|
||||||
@BindView(R.id.name)
|
|
||||||
TextInputEditText name;
|
|
||||||
|
|
||||||
@BindView(R.id.name_layout)
|
|
||||||
TextInputLayout nameLayout;
|
|
||||||
|
|
||||||
private boolean isNewTag;
|
|
||||||
private TagData tagData;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
tagData = getIntent().getParcelableExtra(EXTRA_TAG_DATA);
|
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
if (tagData == null) {
|
|
||||||
isNewTag = true;
|
|
||||||
tagData = new TagData();
|
|
||||||
tagData.setRemoteId(UUIDHelper.newUUID());
|
|
||||||
}
|
|
||||||
if (savedInstanceState == null) {
|
|
||||||
selectedColor = tagData.getColor();
|
|
||||||
selectedIcon = tagData.getIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
name.setText(tagData.getName());
|
|
||||||
|
|
||||||
String autopopulateName = getIntent().getStringExtra(TOKEN_AUTOPOPULATE_NAME);
|
|
||||||
if (!isNullOrEmpty(autopopulateName)) {
|
|
||||||
name.setText(autopopulateName);
|
|
||||||
getIntent().removeExtra(TOKEN_AUTOPOPULATE_NAME);
|
|
||||||
} else if (isNewTag) {
|
|
||||||
name.requestFocus();
|
|
||||||
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
||||||
imm.showSoftInput(name, InputMethodManager.SHOW_IMPLICIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTheme();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isNew() {
|
|
||||||
return tagData == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getToolbarTitle() {
|
|
||||||
return isNew() ? getString(R.string.new_tag) : tagData.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnTextChanged(R.id.name)
|
|
||||||
void onTextChanged() {
|
|
||||||
nameLayout.setError(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getNewName() {
|
|
||||||
return name.getText().toString().trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean clashes(String newName) {
|
|
||||||
return (isNewTag || !newName.equalsIgnoreCase(tagData.getName()))
|
|
||||||
&& tagDataDao.getTagByName(newName) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void save() {
|
|
||||||
String newName = getNewName();
|
|
||||||
|
|
||||||
if (isNullOrEmpty(newName)) {
|
|
||||||
nameLayout.setError(getString(R.string.name_cannot_be_empty));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clashes(newName)) {
|
|
||||||
nameLayout.setError(getString(R.string.tag_already_exists));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNewTag) {
|
|
||||||
tagData.setName(newName);
|
|
||||||
tagData.setColor(selectedColor);
|
|
||||||
tagData.setIcon(selectedIcon);
|
|
||||||
tagDataDao.createNew(tagData);
|
|
||||||
setResult(RESULT_OK, new Intent().putExtra(MainActivity.OPEN_FILTER, new TagFilter(tagData)));
|
|
||||||
} else if (hasChanges()) {
|
|
||||||
tagData.setName(newName);
|
|
||||||
tagData.setColor(selectedColor);
|
|
||||||
tagData.setIcon(selectedIcon);
|
|
||||||
tagDataDao.update(tagData);
|
|
||||||
tagDao.rename(tagData.getRemoteId(), newName);
|
|
||||||
setResult(
|
|
||||||
RESULT_OK,
|
|
||||||
new Intent(TaskListFragment.ACTION_RELOAD)
|
|
||||||
.putExtra(MainActivity.OPEN_FILTER, new TagFilter(tagData)));
|
|
||||||
}
|
|
||||||
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean hasChanges() {
|
|
||||||
if (isNewTag) {
|
|
||||||
return selectedColor >= 0 || selectedIcon >= 0 || !isNullOrEmpty(getNewName());
|
|
||||||
}
|
|
||||||
return !(selectedColor == tagData.getColor()
|
|
||||||
&& selectedIcon == tagData.getIcon()
|
|
||||||
&& getNewName().equals(tagData.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void finish() {
|
|
||||||
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
||||||
imm.hideSoftInputFromWindow(name.getWindowToken(), 0);
|
|
||||||
super.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int getLayout() {
|
|
||||||
return R.layout.activity_tag_settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void delete() {
|
|
||||||
if (tagData != null) {
|
|
||||||
String uuid = tagData.getRemoteId();
|
|
||||||
tagDataDao.delete(tagData);
|
|
||||||
setResult(
|
|
||||||
RESULT_OK,
|
|
||||||
new Intent(TaskListFragment.ACTION_DELETED).putExtra(EXTRA_TAG_UUID, uuid));
|
|
||||||
}
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2012 Todoroo Inc
|
||||||
|
*
|
||||||
|
* See the file "LICENSE" for the full license governing this code.
|
||||||
|
*/
|
||||||
|
package org.tasks.activities
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import butterknife.BindView
|
||||||
|
import butterknife.OnTextChanged
|
||||||
|
import com.google.android.material.textfield.TextInputEditText
|
||||||
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
|
import com.todoroo.astrid.activity.MainActivity
|
||||||
|
import com.todoroo.astrid.activity.TaskListFragment
|
||||||
|
import com.todoroo.astrid.api.TagFilter
|
||||||
|
import com.todoroo.astrid.helper.UUIDHelper
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import org.tasks.R
|
||||||
|
import org.tasks.Strings.isNullOrEmpty
|
||||||
|
import org.tasks.data.TagDaoBlocking
|
||||||
|
import org.tasks.data.TagData
|
||||||
|
import org.tasks.data.TagDataDaoBlocking
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class TagSettingsActivity : BaseListSettingsActivity() {
|
||||||
|
@Inject lateinit var tagDataDao: TagDataDaoBlocking
|
||||||
|
@Inject lateinit var tagDao: TagDaoBlocking
|
||||||
|
|
||||||
|
@BindView(R.id.name)
|
||||||
|
lateinit var name: TextInputEditText
|
||||||
|
|
||||||
|
@BindView(R.id.name_layout)
|
||||||
|
lateinit var nameLayout: TextInputLayout
|
||||||
|
|
||||||
|
private var isNewTag = false
|
||||||
|
private lateinit var tagData: TagData
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
tagData = intent.getParcelableExtra(EXTRA_TAG_DATA)
|
||||||
|
?: TagData().apply {
|
||||||
|
isNewTag = true
|
||||||
|
remoteId = UUIDHelper.newUUID()
|
||||||
|
}
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
selectedColor = tagData.getColor()!!
|
||||||
|
selectedIcon = tagData.getIcon()!!
|
||||||
|
}
|
||||||
|
name.setText(tagData.name)
|
||||||
|
val autopopulateName = intent.getStringExtra(TOKEN_AUTOPOPULATE_NAME)
|
||||||
|
if (!isNullOrEmpty(autopopulateName)) {
|
||||||
|
name.setText(autopopulateName)
|
||||||
|
intent.removeExtra(TOKEN_AUTOPOPULATE_NAME)
|
||||||
|
} else if (isNewTag) {
|
||||||
|
name.requestFocus()
|
||||||
|
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
imm.showSoftInput(name, InputMethodManager.SHOW_IMPLICIT)
|
||||||
|
}
|
||||||
|
updateTheme()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val isNew: Boolean
|
||||||
|
get() = isNewTag
|
||||||
|
|
||||||
|
override val toolbarTitle: String
|
||||||
|
get() = if (isNew) getString(R.string.new_tag) else tagData.name!!
|
||||||
|
|
||||||
|
@OnTextChanged(R.id.name)
|
||||||
|
fun onTextChanged() {
|
||||||
|
nameLayout.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private val newName: String
|
||||||
|
get() = name.text.toString().trim { it <= ' ' }
|
||||||
|
|
||||||
|
private fun clashes(newName: String): Boolean {
|
||||||
|
return ((isNewTag || !newName.equals(tagData.name, ignoreCase = true))
|
||||||
|
&& tagDataDao.getTagByName(newName) != null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun save() {
|
||||||
|
val newName = newName
|
||||||
|
if (isNullOrEmpty(newName)) {
|
||||||
|
nameLayout.error = getString(R.string.name_cannot_be_empty)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (clashes(newName)) {
|
||||||
|
nameLayout.error = getString(R.string.tag_already_exists)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (isNewTag) {
|
||||||
|
tagData.name = newName
|
||||||
|
tagData.setColor(selectedColor)
|
||||||
|
tagData.setIcon(selectedIcon)
|
||||||
|
tagDataDao.createNew(tagData)
|
||||||
|
setResult(Activity.RESULT_OK, Intent().putExtra(MainActivity.OPEN_FILTER, TagFilter(tagData)))
|
||||||
|
} else if (hasChanges()) {
|
||||||
|
tagData.name = newName
|
||||||
|
tagData.setColor(selectedColor)
|
||||||
|
tagData.setIcon(selectedIcon)
|
||||||
|
tagDataDao.update(tagData)
|
||||||
|
tagDao.rename(tagData.remoteId!!, newName)
|
||||||
|
setResult(
|
||||||
|
Activity.RESULT_OK,
|
||||||
|
Intent(TaskListFragment.ACTION_RELOAD)
|
||||||
|
.putExtra(MainActivity.OPEN_FILTER, TagFilter(tagData)))
|
||||||
|
}
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasChanges(): Boolean {
|
||||||
|
return if (isNewTag) {
|
||||||
|
selectedColor >= 0 || selectedIcon >= 0 || !isNullOrEmpty(newName)
|
||||||
|
} else {
|
||||||
|
selectedColor != tagData.getColor()
|
||||||
|
|| selectedIcon != tagData.getIcon()
|
||||||
|
|| newName != tagData.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finish() {
|
||||||
|
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
imm.hideSoftInputFromWindow(name.windowToken, 0)
|
||||||
|
super.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val layout: Int
|
||||||
|
get() = R.layout.activity_tag_settings
|
||||||
|
|
||||||
|
override fun delete() {
|
||||||
|
val uuid = tagData.remoteId
|
||||||
|
tagDataDao.delete(tagData)
|
||||||
|
setResult(
|
||||||
|
Activity.RESULT_OK,
|
||||||
|
Intent(TaskListFragment.ACTION_DELETED).putExtra(EXTRA_TAG_UUID, uuid))
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TOKEN_AUTOPOPULATE_NAME = "autopopulateName" // $NON-NLS-1$
|
||||||
|
const val EXTRA_TAG_DATA = "tagData" // $NON-NLS-1$
|
||||||
|
private const val EXTRA_TAG_UUID = "uuid" // $NON-NLS-1$
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,11 @@
|
|||||||
package org.tasks.caldav
|
package org.tasks.caldav
|
||||||
|
|
||||||
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
import org.tasks.ui.CompletableViewModel
|
import org.tasks.ui.CompletableViewModel
|
||||||
|
|
||||||
class AddCaldavAccountViewModel : CompletableViewModel<String>() {
|
class AddCaldavAccountViewModel @ViewModelInject constructor(
|
||||||
fun addAccount(client: CaldavClient, url: String?, username: String?, password: String?) {
|
private val client: CaldavClient) : CompletableViewModel<String>() {
|
||||||
|
fun addAccount(url: String, username: String, password: String) {
|
||||||
run { client.setForeground().forUrl(url, username, password).homeSet }
|
run { client.setForeground().forUrl(url, username, password).homeSet }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,272 +0,0 @@
|
|||||||
package org.tasks.caldav;
|
|
||||||
|
|
||||||
import static org.tasks.Strings.isNullOrEmpty;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.inputmethod.InputMethodManager;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.ProgressBar;
|
|
||||||
import at.bitfire.dav4jvm.exception.HttpException;
|
|
||||||
import butterknife.BindView;
|
|
||||||
import butterknife.OnTextChanged;
|
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
|
||||||
import com.google.android.material.textfield.TextInputEditText;
|
|
||||||
import com.google.android.material.textfield.TextInputLayout;
|
|
||||||
import com.todoroo.astrid.activity.MainActivity;
|
|
||||||
import com.todoroo.astrid.activity.TaskListFragment;
|
|
||||||
import com.todoroo.astrid.api.CaldavFilter;
|
|
||||||
import com.todoroo.astrid.helper.UUIDHelper;
|
|
||||||
import com.todoroo.astrid.service.TaskDeleter;
|
|
||||||
import java.net.ConnectException;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import kotlin.Unit;
|
|
||||||
import org.tasks.R;
|
|
||||||
import org.tasks.activities.BaseListSettingsActivity;
|
|
||||||
import org.tasks.data.CaldavAccount;
|
|
||||||
import org.tasks.data.CaldavCalendar;
|
|
||||||
import org.tasks.data.CaldavDaoBlocking;
|
|
||||||
import org.tasks.ui.DisplayableException;
|
|
||||||
|
|
||||||
public abstract class BaseCaldavCalendarSettingsActivity extends BaseListSettingsActivity {
|
|
||||||
|
|
||||||
public static final String EXTRA_CALDAV_CALENDAR = "extra_caldav_calendar";
|
|
||||||
public static final String EXTRA_CALDAV_ACCOUNT = "extra_caldav_account";
|
|
||||||
|
|
||||||
@Inject protected CaldavDaoBlocking caldavDao;
|
|
||||||
@Inject TaskDeleter taskDeleter;
|
|
||||||
|
|
||||||
@BindView(R.id.root_layout)
|
|
||||||
LinearLayout root;
|
|
||||||
|
|
||||||
@BindView(R.id.name)
|
|
||||||
TextInputEditText name;
|
|
||||||
|
|
||||||
@BindView(R.id.name_layout)
|
|
||||||
TextInputLayout nameLayout;
|
|
||||||
|
|
||||||
@BindView(R.id.progress_bar)
|
|
||||||
ProgressBar progressView;
|
|
||||||
|
|
||||||
private CaldavCalendar caldavCalendar;
|
|
||||||
private CaldavAccount caldavAccount;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int getLayout() {
|
|
||||||
return R.layout.activity_caldav_calendar_settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
Intent intent = getIntent();
|
|
||||||
caldavCalendar = intent.getParcelableExtra(EXTRA_CALDAV_CALENDAR);
|
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
if (caldavCalendar == null) {
|
|
||||||
caldavAccount = intent.getParcelableExtra(EXTRA_CALDAV_ACCOUNT);
|
|
||||||
} else {
|
|
||||||
caldavAccount = caldavDao.getAccountByUuid(caldavCalendar.getAccount());
|
|
||||||
}
|
|
||||||
caldavAccount =
|
|
||||||
caldavCalendar == null
|
|
||||||
? intent.getParcelableExtra(EXTRA_CALDAV_ACCOUNT)
|
|
||||||
: caldavDao.getAccountByUuid(caldavCalendar.getAccount());
|
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
|
||||||
if (caldavCalendar != null) {
|
|
||||||
name.setText(caldavCalendar.getName());
|
|
||||||
selectedColor = caldavCalendar.getColor();
|
|
||||||
selectedIcon = caldavCalendar.getIcon();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (caldavCalendar == null) {
|
|
||||||
name.requestFocus();
|
|
||||||
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
||||||
imm.showSoftInput(name, InputMethodManager.SHOW_IMPLICIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTheme();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isNew() {
|
|
||||||
return caldavCalendar == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getToolbarTitle() {
|
|
||||||
return isNew() ? getString(R.string.new_list) : caldavCalendar.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnTextChanged(R.id.name)
|
|
||||||
void onNameChanged() {
|
|
||||||
nameLayout.setError(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void save() {
|
|
||||||
if (requestInProgress()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String name = getNewName();
|
|
||||||
|
|
||||||
if (isNullOrEmpty(name)) {
|
|
||||||
nameLayout.setError(getString(R.string.name_cannot_be_empty));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (caldavCalendar == null) {
|
|
||||||
showProgressIndicator();
|
|
||||||
createCalendar(caldavAccount, name, selectedColor);
|
|
||||||
} else if (nameChanged() || colorChanged()) {
|
|
||||||
showProgressIndicator();
|
|
||||||
updateNameAndColor(caldavAccount, caldavCalendar, name, selectedColor);
|
|
||||||
} else if (iconChanged()) {
|
|
||||||
updateCalendar();
|
|
||||||
} else {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void createCalendar(CaldavAccount caldavAccount, String name, int color);
|
|
||||||
|
|
||||||
protected abstract void updateNameAndColor(
|
|
||||||
CaldavAccount account, CaldavCalendar calendar, String name, int color);
|
|
||||||
|
|
||||||
protected abstract void deleteCalendar(
|
|
||||||
CaldavAccount caldavAccount, CaldavCalendar caldavCalendar);
|
|
||||||
|
|
||||||
private void showProgressIndicator() {
|
|
||||||
progressView.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void hideProgressIndicator() {
|
|
||||||
progressView.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean requestInProgress() {
|
|
||||||
return progressView.getVisibility() == View.VISIBLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Unit requestFailed(Throwable t) {
|
|
||||||
hideProgressIndicator();
|
|
||||||
|
|
||||||
if (t instanceof HttpException) {
|
|
||||||
showSnackbar(t.getMessage());
|
|
||||||
} else if (t instanceof DisplayableException) {
|
|
||||||
showSnackbar(((DisplayableException) t).getResId());
|
|
||||||
} else if (t instanceof ConnectException) {
|
|
||||||
showSnackbar(R.string.network_error);
|
|
||||||
} else {
|
|
||||||
showSnackbar(R.string.error_adding_account, t.getMessage());
|
|
||||||
}
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showSnackbar(int resId, Object... formatArgs) {
|
|
||||||
showSnackbar(getString(resId, formatArgs));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showSnackbar(String message) {
|
|
||||||
Snackbar snackbar =
|
|
||||||
Snackbar.make(root, message, 8000)
|
|
||||||
.setTextColor(getColor(R.color.snackbar_text_color))
|
|
||||||
.setActionTextColor(getColor(R.color.snackbar_action_color));
|
|
||||||
snackbar
|
|
||||||
.getView()
|
|
||||||
.setBackgroundColor(getColor(R.color.snackbar_background));
|
|
||||||
snackbar.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Unit createSuccessful(String url) {
|
|
||||||
CaldavCalendar caldavCalendar = new CaldavCalendar();
|
|
||||||
caldavCalendar.setUuid(UUIDHelper.newUUID());
|
|
||||||
caldavCalendar.setAccount(caldavAccount.getUuid());
|
|
||||||
caldavCalendar.setUrl(url);
|
|
||||||
caldavCalendar.setName(getNewName());
|
|
||||||
caldavCalendar.setColor(selectedColor);
|
|
||||||
caldavCalendar.setIcon(selectedIcon);
|
|
||||||
caldavDao.insert(caldavCalendar);
|
|
||||||
setResult(
|
|
||||||
RESULT_OK,
|
|
||||||
new Intent().putExtra(MainActivity.OPEN_FILTER, new CaldavFilter(caldavCalendar)));
|
|
||||||
finish();
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Unit updateCalendar() {
|
|
||||||
caldavCalendar.setName(getNewName());
|
|
||||||
caldavCalendar.setColor(selectedColor);
|
|
||||||
caldavCalendar.setIcon(selectedIcon);
|
|
||||||
caldavDao.update(caldavCalendar);
|
|
||||||
setResult(
|
|
||||||
RESULT_OK,
|
|
||||||
new Intent(TaskListFragment.ACTION_RELOAD)
|
|
||||||
.putExtra(MainActivity.OPEN_FILTER, new CaldavFilter(caldavCalendar)));
|
|
||||||
finish();
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean hasChanges() {
|
|
||||||
return caldavCalendar == null
|
|
||||||
? !isNullOrEmpty(getNewName()) || selectedColor != 0 || selectedIcon != -1
|
|
||||||
: nameChanged() || iconChanged() || colorChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean nameChanged() {
|
|
||||||
return !caldavCalendar.getName().equals(getNewName());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean colorChanged() {
|
|
||||||
return selectedColor != caldavCalendar.getColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean iconChanged() {
|
|
||||||
return selectedIcon != caldavCalendar.getIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getNewName() {
|
|
||||||
return name.getText().toString().trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void finish() {
|
|
||||||
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
||||||
imm.hideSoftInputFromWindow(name.getWindowToken(), 0);
|
|
||||||
super.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void discard() {
|
|
||||||
if (!requestInProgress()) {
|
|
||||||
super.discard();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void promptDelete() {
|
|
||||||
if (!requestInProgress()) {
|
|
||||||
super.promptDelete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void delete() {
|
|
||||||
showProgressIndicator();
|
|
||||||
deleteCalendar(caldavAccount, caldavCalendar);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onDeleted(boolean deleted) {
|
|
||||||
if (deleted) {
|
|
||||||
taskDeleter.delete(caldavCalendar);
|
|
||||||
setResult(RESULT_OK, new Intent(TaskListFragment.ACTION_DELETED));
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,239 @@
|
|||||||
|
package org.tasks.caldav
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.ProgressBar
|
||||||
|
import at.bitfire.dav4jvm.exception.HttpException
|
||||||
|
import butterknife.BindView
|
||||||
|
import butterknife.OnTextChanged
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import com.google.android.material.textfield.TextInputEditText
|
||||||
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
|
import com.todoroo.astrid.activity.MainActivity
|
||||||
|
import com.todoroo.astrid.activity.TaskListFragment
|
||||||
|
import com.todoroo.astrid.api.CaldavFilter
|
||||||
|
import com.todoroo.astrid.helper.UUIDHelper
|
||||||
|
import com.todoroo.astrid.service.TaskDeleter
|
||||||
|
import org.tasks.R
|
||||||
|
import org.tasks.Strings.isNullOrEmpty
|
||||||
|
import org.tasks.activities.BaseListSettingsActivity
|
||||||
|
import org.tasks.data.CaldavAccount
|
||||||
|
import org.tasks.data.CaldavCalendar
|
||||||
|
import org.tasks.data.CaldavDaoBlocking
|
||||||
|
import org.tasks.ui.DisplayableException
|
||||||
|
import java.net.ConnectException
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
abstract class BaseCaldavCalendarSettingsActivity : BaseListSettingsActivity() {
|
||||||
|
@Inject lateinit var caldavDao: CaldavDaoBlocking
|
||||||
|
@Inject lateinit var taskDeleter: TaskDeleter
|
||||||
|
|
||||||
|
@BindView(R.id.root_layout)
|
||||||
|
lateinit var root: LinearLayout
|
||||||
|
|
||||||
|
@BindView(R.id.name)
|
||||||
|
lateinit var name: TextInputEditText
|
||||||
|
|
||||||
|
@BindView(R.id.name_layout)
|
||||||
|
lateinit var nameLayout: TextInputLayout
|
||||||
|
|
||||||
|
@BindView(R.id.progress_bar)
|
||||||
|
lateinit var progressView: ProgressBar
|
||||||
|
|
||||||
|
private var caldavCalendar: CaldavCalendar? = null
|
||||||
|
private lateinit var caldavAccount: CaldavAccount
|
||||||
|
|
||||||
|
override val layout: Int
|
||||||
|
get() = R.layout.activity_caldav_calendar_settings
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
val intent = intent
|
||||||
|
caldavCalendar = intent.getParcelableExtra(EXTRA_CALDAV_CALENDAR)
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
caldavAccount = if (caldavCalendar == null) {
|
||||||
|
intent.getParcelableExtra(EXTRA_CALDAV_ACCOUNT)!!
|
||||||
|
} else {
|
||||||
|
caldavDao.getAccountByUuid(caldavCalendar!!.account!!)!!
|
||||||
|
}
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
if (caldavCalendar != null) {
|
||||||
|
name.setText(caldavCalendar!!.name)
|
||||||
|
selectedColor = caldavCalendar!!.color
|
||||||
|
selectedIcon = caldavCalendar!!.getIcon()!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (caldavCalendar == null) {
|
||||||
|
name.requestFocus()
|
||||||
|
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
imm.showSoftInput(name, InputMethodManager.SHOW_IMPLICIT)
|
||||||
|
}
|
||||||
|
updateTheme()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val isNew: Boolean
|
||||||
|
get() = caldavCalendar == null
|
||||||
|
|
||||||
|
override val toolbarTitle: String
|
||||||
|
get() = if (isNew) getString(R.string.new_list) else caldavCalendar!!.name!!
|
||||||
|
|
||||||
|
@OnTextChanged(R.id.name)
|
||||||
|
fun onNameChanged() {
|
||||||
|
nameLayout.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun save() {
|
||||||
|
if (requestInProgress()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val name = newName
|
||||||
|
if (isNullOrEmpty(name)) {
|
||||||
|
nameLayout.error = getString(R.string.name_cannot_be_empty)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (caldavCalendar == null) {
|
||||||
|
showProgressIndicator()
|
||||||
|
createCalendar(caldavAccount, name, selectedColor)
|
||||||
|
} else if (nameChanged() || colorChanged()) {
|
||||||
|
showProgressIndicator()
|
||||||
|
updateNameAndColor(caldavAccount, caldavCalendar!!, name, selectedColor)
|
||||||
|
} else if (iconChanged()) {
|
||||||
|
updateCalendar()
|
||||||
|
} else {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun createCalendar(caldavAccount: CaldavAccount, name: String, color: Int)
|
||||||
|
|
||||||
|
protected abstract fun updateNameAndColor(
|
||||||
|
account: CaldavAccount, calendar: CaldavCalendar, name: String, color: Int)
|
||||||
|
|
||||||
|
protected abstract fun deleteCalendar(
|
||||||
|
caldavAccount: CaldavAccount, caldavCalendar: CaldavCalendar)
|
||||||
|
|
||||||
|
private fun showProgressIndicator() {
|
||||||
|
progressView.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideProgressIndicator() {
|
||||||
|
progressView.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun requestInProgress(): Boolean {
|
||||||
|
return progressView.visibility == View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun requestFailed(t: Throwable) {
|
||||||
|
hideProgressIndicator()
|
||||||
|
when (t) {
|
||||||
|
is HttpException -> showSnackbar(t.message)
|
||||||
|
is DisplayableException -> showSnackbar(t.resId)
|
||||||
|
is ConnectException -> showSnackbar(R.string.network_error)
|
||||||
|
else -> showSnackbar(R.string.error_adding_account, t.message!!)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showSnackbar(resId: Int, vararg formatArgs: Any) {
|
||||||
|
showSnackbar(getString(resId, *formatArgs))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showSnackbar(message: String?) {
|
||||||
|
val snackbar = Snackbar.make(root, message!!, 8000)
|
||||||
|
.setTextColor(getColor(R.color.snackbar_text_color))
|
||||||
|
.setActionTextColor(getColor(R.color.snackbar_action_color))
|
||||||
|
snackbar
|
||||||
|
.view
|
||||||
|
.setBackgroundColor(getColor(R.color.snackbar_background))
|
||||||
|
snackbar.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun createSuccessful(url: String?) {
|
||||||
|
val caldavCalendar = CaldavCalendar()
|
||||||
|
caldavCalendar.uuid = UUIDHelper.newUUID()
|
||||||
|
caldavCalendar.account = caldavAccount.uuid
|
||||||
|
caldavCalendar.url = url
|
||||||
|
caldavCalendar.name = newName
|
||||||
|
caldavCalendar.color = selectedColor
|
||||||
|
caldavCalendar.setIcon(selectedIcon)
|
||||||
|
caldavDao.insert(caldavCalendar)
|
||||||
|
setResult(
|
||||||
|
Activity.RESULT_OK,
|
||||||
|
Intent().putExtra(MainActivity.OPEN_FILTER, CaldavFilter(caldavCalendar)))
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun updateCalendar() {
|
||||||
|
caldavCalendar!!.name = newName
|
||||||
|
caldavCalendar!!.color = selectedColor
|
||||||
|
caldavCalendar!!.setIcon(selectedIcon)
|
||||||
|
caldavDao.update(caldavCalendar!!)
|
||||||
|
setResult(
|
||||||
|
Activity.RESULT_OK,
|
||||||
|
Intent(TaskListFragment.ACTION_RELOAD)
|
||||||
|
.putExtra(MainActivity.OPEN_FILTER, CaldavFilter(caldavCalendar)))
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasChanges(): Boolean {
|
||||||
|
return if (caldavCalendar == null) !isNullOrEmpty(newName) || selectedColor != 0 || selectedIcon != -1 else nameChanged() || iconChanged() || colorChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun nameChanged(): Boolean {
|
||||||
|
return caldavCalendar!!.name != newName
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun colorChanged(): Boolean {
|
||||||
|
return selectedColor != caldavCalendar!!.color
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun iconChanged(): Boolean {
|
||||||
|
return selectedIcon != caldavCalendar!!.getIcon()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val newName: String
|
||||||
|
get() = name.text.toString().trim { it <= ' ' }
|
||||||
|
|
||||||
|
override fun finish() {
|
||||||
|
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
imm.hideSoftInputFromWindow(name.windowToken, 0)
|
||||||
|
super.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun discard() {
|
||||||
|
if (!requestInProgress()) {
|
||||||
|
super.discard()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun promptDelete() {
|
||||||
|
if (!requestInProgress()) {
|
||||||
|
super.promptDelete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun delete() {
|
||||||
|
showProgressIndicator()
|
||||||
|
deleteCalendar(caldavAccount, caldavCalendar!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun onDeleted(deleted: Boolean) {
|
||||||
|
if (deleted) {
|
||||||
|
taskDeleter.delete(caldavCalendar!!)
|
||||||
|
setResult(Activity.RESULT_OK, Intent(TaskListFragment.ACTION_DELETED))
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val EXTRA_CALDAV_CALENDAR = "extra_caldav_calendar"
|
||||||
|
const val EXTRA_CALDAV_ACCOUNT = "extra_caldav_account"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,54 +0,0 @@
|
|||||||
package org.tasks.caldav;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.tasks.R;
|
|
||||||
import org.tasks.data.CaldavAccount;
|
|
||||||
import org.tasks.data.CaldavCalendar;
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
public class CaldavCalendarSettingsActivity extends BaseCaldavCalendarSettingsActivity {
|
|
||||||
|
|
||||||
@Inject CaldavClient client;
|
|
||||||
|
|
||||||
private CreateCalendarViewModel createCalendarViewModel;
|
|
||||||
private DeleteCalendarViewModel deleteCalendarViewModel;
|
|
||||||
private UpdateCalendarViewModel updateCalendarViewModel;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int getLayout() {
|
|
||||||
return R.layout.activity_caldav_calendar_settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
ViewModelProvider provider = new ViewModelProvider(this);
|
|
||||||
createCalendarViewModel = provider.get(CreateCalendarViewModel.class);
|
|
||||||
deleteCalendarViewModel = provider.get(DeleteCalendarViewModel.class);
|
|
||||||
updateCalendarViewModel = provider.get(UpdateCalendarViewModel.class);
|
|
||||||
|
|
||||||
createCalendarViewModel.observe(this, this::createSuccessful, this::requestFailed);
|
|
||||||
deleteCalendarViewModel.observe(this, this::onDeleted, this::requestFailed);
|
|
||||||
updateCalendarViewModel.observe(this, ignored -> updateCalendar(), this::requestFailed);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void createCalendar(CaldavAccount caldavAccount, String name, int color) {
|
|
||||||
createCalendarViewModel.createCalendar(client, caldavAccount, name, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void updateNameAndColor(
|
|
||||||
CaldavAccount account, CaldavCalendar calendar, String name, int color) {
|
|
||||||
updateCalendarViewModel.updateCalendar(client, account, calendar, name, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void deleteCalendar(CaldavAccount caldavAccount, CaldavCalendar caldavCalendar) {
|
|
||||||
deleteCalendarViewModel.deleteCalendar(client, caldavAccount, caldavCalendar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package org.tasks.caldav
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import org.tasks.R
|
||||||
|
import org.tasks.data.CaldavAccount
|
||||||
|
import org.tasks.data.CaldavCalendar
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class CaldavCalendarSettingsActivity : BaseCaldavCalendarSettingsActivity() {
|
||||||
|
private val createCalendarViewModel: CreateCalendarViewModel by viewModels()
|
||||||
|
private val deleteCalendarViewModel: DeleteCalendarViewModel by viewModels()
|
||||||
|
private val updateCalendarViewModel: UpdateCalendarViewModel by viewModels()
|
||||||
|
|
||||||
|
override val layout: Int
|
||||||
|
get() = R.layout.activity_caldav_calendar_settings
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
createCalendarViewModel.observe(this, this::createSuccessful, this::requestFailed)
|
||||||
|
deleteCalendarViewModel.observe(this, this::onDeleted, this::requestFailed)
|
||||||
|
updateCalendarViewModel.observe(this, { updateCalendar() }, this::requestFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createCalendar(caldavAccount: CaldavAccount, name: String, color: Int) =
|
||||||
|
createCalendarViewModel.createCalendar(caldavAccount, name, color)
|
||||||
|
|
||||||
|
override fun updateNameAndColor(
|
||||||
|
account: CaldavAccount, calendar: CaldavCalendar, name: String, color: Int) =
|
||||||
|
updateCalendarViewModel.updateCalendar(account, calendar, name, color)
|
||||||
|
|
||||||
|
override fun deleteCalendar(caldavAccount: CaldavAccount, caldavCalendar: CaldavCalendar) =
|
||||||
|
deleteCalendarViewModel.deleteCalendar(caldavAccount, caldavCalendar)
|
||||||
|
}
|
||||||
@ -1,10 +1,12 @@
|
|||||||
package org.tasks.caldav
|
package org.tasks.caldav
|
||||||
|
|
||||||
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
import org.tasks.data.CaldavAccount
|
import org.tasks.data.CaldavAccount
|
||||||
import org.tasks.ui.CompletableViewModel
|
import org.tasks.ui.CompletableViewModel
|
||||||
|
|
||||||
class CreateCalendarViewModel : CompletableViewModel<String?>() {
|
class CreateCalendarViewModel @ViewModelInject constructor(
|
||||||
fun createCalendar(client: CaldavClient, account: CaldavAccount?, name: String?, color: Int) {
|
private val client: CaldavClient): CompletableViewModel<String?>() {
|
||||||
|
fun createCalendar(account: CaldavAccount, name: String, color: Int) {
|
||||||
run { client.forAccount(account).makeCollection(name, color) }
|
run { client.forAccount(account).makeCollection(name, color) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,12 +0,0 @@
|
|||||||
package org.tasks.caldav;
|
|
||||||
|
|
||||||
import org.tasks.data.CaldavAccount;
|
|
||||||
import org.tasks.data.CaldavCalendar;
|
|
||||||
import org.tasks.ui.ActionViewModel;
|
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
|
||||||
public class DeleteCalendarViewModel extends ActionViewModel {
|
|
||||||
void deleteCalendar(CaldavClient client, CaldavAccount account, CaldavCalendar calendar) {
|
|
||||||
run(() -> client.forCalendar(account, calendar).deleteCollection());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package org.tasks.caldav
|
||||||
|
|
||||||
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
|
import org.tasks.data.CaldavAccount
|
||||||
|
import org.tasks.data.CaldavCalendar
|
||||||
|
import org.tasks.ui.ActionViewModel
|
||||||
|
|
||||||
|
class DeleteCalendarViewModel @ViewModelInject constructor(
|
||||||
|
private val client: CaldavClient) : ActionViewModel() {
|
||||||
|
fun deleteCalendar(account: CaldavAccount, calendar: CaldavCalendar) {
|
||||||
|
run { client.forCalendar(account, calendar).deleteCollection() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +1,11 @@
|
|||||||
package org.tasks.caldav
|
package org.tasks.caldav
|
||||||
|
|
||||||
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
import org.tasks.ui.CompletableViewModel
|
import org.tasks.ui.CompletableViewModel
|
||||||
|
|
||||||
class UpdateCaldavAccountViewModel : CompletableViewModel<String>() {
|
class UpdateCaldavAccountViewModel @ViewModelInject constructor(
|
||||||
fun updateCaldavAccount(
|
private val client: CaldavClient) : CompletableViewModel<String>() {
|
||||||
client: CaldavClient, url: String?, username: String?, password: String?) {
|
fun updateCaldavAccount(url: String, username: String, password: String?) {
|
||||||
run {
|
run { client.forUrl(url, username, password).homeSet }
|
||||||
client.forUrl(url, username, password).homeSet
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,12 +1,13 @@
|
|||||||
package org.tasks.caldav
|
package org.tasks.caldav
|
||||||
|
|
||||||
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
import org.tasks.data.CaldavAccount
|
import org.tasks.data.CaldavAccount
|
||||||
import org.tasks.data.CaldavCalendar
|
import org.tasks.data.CaldavCalendar
|
||||||
import org.tasks.ui.CompletableViewModel
|
import org.tasks.ui.CompletableViewModel
|
||||||
|
|
||||||
class UpdateCalendarViewModel : CompletableViewModel<String?>() {
|
class UpdateCalendarViewModel @ViewModelInject constructor(
|
||||||
fun updateCalendar(
|
private val client: CaldavClient) : CompletableViewModel<String?>() {
|
||||||
client: CaldavClient, account: CaldavAccount?, calendar: CaldavCalendar?, name: String?, color: Int) {
|
fun updateCalendar(account: CaldavAccount, calendar: CaldavCalendar, name: String, color: Int) {
|
||||||
run { client.forCalendar(account, calendar).updateCollection(name, color) }
|
run { client.forCalendar(account, calendar).updateCollection(name, color) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,10 +1,12 @@
|
|||||||
package org.tasks.etesync
|
package org.tasks.etesync
|
||||||
|
|
||||||
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
import org.tasks.data.CaldavAccount
|
import org.tasks.data.CaldavAccount
|
||||||
import org.tasks.ui.CompletableViewModel
|
import org.tasks.ui.CompletableViewModel
|
||||||
|
|
||||||
class CreateCalendarViewModel : CompletableViewModel<String?>() {
|
class CreateCalendarViewModel @ViewModelInject constructor(
|
||||||
fun createCalendar(client: EteSyncClient, account: CaldavAccount?, name: String?, color: Int) {
|
private val client: EteSyncClient) : CompletableViewModel<String?>() {
|
||||||
run { client.forAccount(account!!).makeCollection(name, color) }
|
fun createCalendar(account: CaldavAccount, name: String, color: Int) {
|
||||||
|
run { client.forAccount(account).makeCollection(name, color) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,12 +1,14 @@
|
|||||||
package org.tasks.etesync
|
package org.tasks.etesync
|
||||||
|
|
||||||
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
import org.tasks.data.CaldavAccount
|
import org.tasks.data.CaldavAccount
|
||||||
import org.tasks.ui.CompletableViewModel
|
import org.tasks.ui.CompletableViewModel
|
||||||
|
|
||||||
class CreateUserInfoViewModel : CompletableViewModel<String?>() {
|
class CreateUserInfoViewModel @ViewModelInject constructor(
|
||||||
fun createUserInfo(client: EteSyncClient, caldavAccount: CaldavAccount?, derivedKey: String?) {
|
private val client: EteSyncClient): CompletableViewModel<String>() {
|
||||||
|
fun createUserInfo(caldavAccount: CaldavAccount, derivedKey: String) {
|
||||||
run {
|
run {
|
||||||
client.forAccount(caldavAccount!!).createUserInfo(derivedKey)
|
client.forAccount(caldavAccount).createUserInfo(derivedKey)
|
||||||
derivedKey
|
derivedKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
package org.tasks.etesync;
|
|
||||||
|
|
||||||
import org.tasks.data.CaldavAccount;
|
|
||||||
import org.tasks.data.CaldavCalendar;
|
|
||||||
import org.tasks.ui.ActionViewModel;
|
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
|
||||||
public class DeleteCalendarViewModel extends ActionViewModel {
|
|
||||||
void deleteCalendar(EteSyncClient client, CaldavAccount account, CaldavCalendar calendar) {
|
|
||||||
run(() -> client.forAccount(account).deleteCollection(calendar));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package org.tasks.etesync
|
||||||
|
|
||||||
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
|
import org.tasks.data.CaldavAccount
|
||||||
|
import org.tasks.data.CaldavCalendar
|
||||||
|
import org.tasks.ui.ActionViewModel
|
||||||
|
|
||||||
|
class DeleteCalendarViewModel @ViewModelInject constructor(
|
||||||
|
private val client: EteSyncClient) : ActionViewModel() {
|
||||||
|
fun deleteCalendar(account: CaldavAccount, calendar: CaldavCalendar) {
|
||||||
|
run { client.forAccount(account).deleteCollection(calendar) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,221 +0,0 @@
|
|||||||
package org.tasks.etesync;
|
|
||||||
|
|
||||||
import static org.tasks.Strings.isNullOrEmpty;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import androidx.appcompat.widget.Toolbar;
|
|
||||||
import androidx.appcompat.widget.Toolbar.OnMenuItemClickListener;
|
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
|
||||||
import at.bitfire.dav4jvm.exception.HttpException;
|
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import butterknife.OnTextChanged;
|
|
||||||
import com.etesync.journalmanager.Constants;
|
|
||||||
import com.etesync.journalmanager.Crypto;
|
|
||||||
import com.etesync.journalmanager.Crypto.CryptoManager;
|
|
||||||
import com.etesync.journalmanager.Exceptions.IntegrityException;
|
|
||||||
import com.etesync.journalmanager.Exceptions.VersionTooNewException;
|
|
||||||
import com.etesync.journalmanager.UserInfoManager.UserInfo;
|
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint;
|
|
||||||
import java.net.ConnectException;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import kotlin.Unit;
|
|
||||||
import org.tasks.R;
|
|
||||||
import org.tasks.data.CaldavAccount;
|
|
||||||
import org.tasks.databinding.ActivityEtesyncEncryptionSettingsBinding;
|
|
||||||
import org.tasks.injection.ThemedInjectingAppCompatActivity;
|
|
||||||
import org.tasks.security.KeyStoreEncryption;
|
|
||||||
import org.tasks.ui.DisplayableException;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
public class EncryptionSettingsActivity extends ThemedInjectingAppCompatActivity
|
|
||||||
implements OnMenuItemClickListener {
|
|
||||||
|
|
||||||
public static final String EXTRA_USER_INFO = "extra_user_info";
|
|
||||||
public static final String EXTRA_ACCOUNT = "extra_account";
|
|
||||||
public static final String EXTRA_DERIVED_KEY = "extra_derived_key";
|
|
||||||
|
|
||||||
@Inject EteSyncClient client;
|
|
||||||
@Inject KeyStoreEncryption encryption;
|
|
||||||
|
|
||||||
private ActivityEtesyncEncryptionSettingsBinding binding;
|
|
||||||
private UserInfo userInfo;
|
|
||||||
private CaldavAccount caldavAccount;
|
|
||||||
private CreateUserInfoViewModel createUserInfoViewModel;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
createUserInfoViewModel = new ViewModelProvider(this).get(CreateUserInfoViewModel.class);
|
|
||||||
|
|
||||||
binding = ActivityEtesyncEncryptionSettingsBinding.inflate(getLayoutInflater());
|
|
||||||
setContentView(binding.getRoot());
|
|
||||||
|
|
||||||
ButterKnife.bind(this);
|
|
||||||
|
|
||||||
Intent intent = getIntent();
|
|
||||||
caldavAccount = intent.getParcelableExtra(EXTRA_ACCOUNT);
|
|
||||||
userInfo = (UserInfo) intent.getSerializableExtra(EXTRA_USER_INFO);
|
|
||||||
|
|
||||||
if (userInfo == null) {
|
|
||||||
binding.description.setVisibility(View.VISIBLE);
|
|
||||||
binding.repeatEncryptionPasswordLayout.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
Toolbar toolbar = binding.toolbar.toolbar;
|
|
||||||
toolbar.setTitle(
|
|
||||||
caldavAccount == null ? getString(R.string.add_account) : caldavAccount.getName());
|
|
||||||
toolbar.setNavigationIcon(getDrawable(R.drawable.ic_outline_save_24px));
|
|
||||||
toolbar.setNavigationOnClickListener(v -> save());
|
|
||||||
toolbar.inflateMenu(R.menu.menu_help);
|
|
||||||
toolbar.setOnMenuItemClickListener(this);
|
|
||||||
themeColor.apply(toolbar);
|
|
||||||
|
|
||||||
createUserInfoViewModel.observe(this, this::returnDerivedKey, this::requestFailed);
|
|
||||||
|
|
||||||
if (createUserInfoViewModel.inProgress()) {
|
|
||||||
showProgressIndicator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showProgressIndicator() {
|
|
||||||
binding.progressBar.progressBar.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void hideProgressIndicator() {
|
|
||||||
binding.progressBar.progressBar.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean requestInProgress() {
|
|
||||||
return binding.progressBar.progressBar.getVisibility() == View.VISIBLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Unit returnDerivedKey(String derivedKey) {
|
|
||||||
hideProgressIndicator();
|
|
||||||
|
|
||||||
Intent result = new Intent();
|
|
||||||
result.putExtra(EXTRA_DERIVED_KEY, derivedKey);
|
|
||||||
setResult(RESULT_OK, result);
|
|
||||||
finish();
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void save() {
|
|
||||||
if (requestInProgress()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String encryptionPassword = getNewEncryptionPassword();
|
|
||||||
String derivedKey = caldavAccount.getEncryptionPassword(encryption);
|
|
||||||
|
|
||||||
if (isNullOrEmpty(encryptionPassword) && isNullOrEmpty(derivedKey)) {
|
|
||||||
binding.encryptionPasswordLayout.setError(getString(R.string.encryption_password_required));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userInfo == null) {
|
|
||||||
String repeatEncryptionPassword = binding.repeatEncryptionPassword.getText().toString().trim();
|
|
||||||
if (!encryptionPassword.equals(repeatEncryptionPassword)) {
|
|
||||||
binding.repeatEncryptionPasswordLayout.setError(getString(R.string.passwords_do_not_match));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String key =
|
|
||||||
isNullOrEmpty(encryptionPassword)
|
|
||||||
? derivedKey
|
|
||||||
: Crypto.deriveKey(caldavAccount.getUsername(), encryptionPassword);
|
|
||||||
CryptoManager cryptoManager;
|
|
||||||
try {
|
|
||||||
int version = userInfo == null ? Constants.CURRENT_VERSION : userInfo.getVersion();
|
|
||||||
cryptoManager = new CryptoManager(version, key, "userInfo");
|
|
||||||
} catch (VersionTooNewException | IntegrityException e) {
|
|
||||||
requestFailed(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userInfo == null) {
|
|
||||||
showProgressIndicator();
|
|
||||||
createUserInfoViewModel.createUserInfo(client, caldavAccount, key);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
userInfo.verify(cryptoManager);
|
|
||||||
returnDerivedKey(key);
|
|
||||||
} catch (IntegrityException e) {
|
|
||||||
binding.encryptionPasswordLayout.setError(getString(R.string.encryption_password_wrong));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Unit requestFailed(Throwable t) {
|
|
||||||
hideProgressIndicator();
|
|
||||||
|
|
||||||
if (t instanceof HttpException) {
|
|
||||||
showSnackbar(t.getMessage());
|
|
||||||
} else if (t instanceof DisplayableException) {
|
|
||||||
showSnackbar(((DisplayableException) t).getResId());
|
|
||||||
} else if (t instanceof ConnectException) {
|
|
||||||
showSnackbar(R.string.network_error);
|
|
||||||
} else {
|
|
||||||
Timber.e(t);
|
|
||||||
showSnackbar(R.string.error_adding_account, t.getMessage());
|
|
||||||
}
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showSnackbar(int resId, Object... formatArgs) {
|
|
||||||
showSnackbar(getString(resId, formatArgs));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showSnackbar(String message) {
|
|
||||||
newSnackbar(message).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Snackbar newSnackbar(String message) {
|
|
||||||
Snackbar snackbar =
|
|
||||||
Snackbar.make(binding.rootLayout, message, 8000)
|
|
||||||
.setTextColor(getColor(R.color.snackbar_text_color))
|
|
||||||
.setActionTextColor(getColor(R.color.snackbar_action_color));
|
|
||||||
snackbar
|
|
||||||
.getView()
|
|
||||||
.setBackgroundColor(getColor(R.color.snackbar_background));
|
|
||||||
return snackbar;
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnTextChanged(R.id.repeat_encryption_password)
|
|
||||||
void onRpeatEncryptionPasswordChanged() {
|
|
||||||
binding.repeatEncryptionPasswordLayout.setError(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnTextChanged(R.id.encryption_password)
|
|
||||||
void onEncryptionPasswordChanged() {
|
|
||||||
binding.encryptionPasswordLayout.setError(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getNewEncryptionPassword() {
|
|
||||||
return binding.encryptionPassword.getText().toString().trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void finish() {
|
|
||||||
if (!requestInProgress()) {
|
|
||||||
super.finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onMenuItemClick(MenuItem item) {
|
|
||||||
if (item.getItemId() == R.id.menu_help) {
|
|
||||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://tasks.org/etesync")));
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,186 @@
|
|||||||
|
package org.tasks.etesync
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.appcompat.widget.Toolbar
|
||||||
|
import at.bitfire.dav4jvm.exception.HttpException
|
||||||
|
import butterknife.ButterKnife
|
||||||
|
import butterknife.OnTextChanged
|
||||||
|
import com.etesync.journalmanager.Constants.Companion.CURRENT_VERSION
|
||||||
|
import com.etesync.journalmanager.Crypto.CryptoManager
|
||||||
|
import com.etesync.journalmanager.Crypto.deriveKey
|
||||||
|
import com.etesync.journalmanager.Exceptions.IntegrityException
|
||||||
|
import com.etesync.journalmanager.Exceptions.VersionTooNewException
|
||||||
|
import com.etesync.journalmanager.UserInfoManager
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import org.tasks.R
|
||||||
|
import org.tasks.Strings.isNullOrEmpty
|
||||||
|
import org.tasks.data.CaldavAccount
|
||||||
|
import org.tasks.databinding.ActivityEtesyncEncryptionSettingsBinding
|
||||||
|
import org.tasks.injection.ThemedInjectingAppCompatActivity
|
||||||
|
import org.tasks.security.KeyStoreEncryption
|
||||||
|
import org.tasks.ui.DisplayableException
|
||||||
|
import java.net.ConnectException
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class EncryptionSettingsActivity : ThemedInjectingAppCompatActivity(), Toolbar.OnMenuItemClickListener {
|
||||||
|
@Inject lateinit var encryption: KeyStoreEncryption
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityEtesyncEncryptionSettingsBinding
|
||||||
|
private var userInfo: UserInfoManager.UserInfo? = null
|
||||||
|
private var caldavAccount: CaldavAccount? = null
|
||||||
|
private val createUserInfoViewModel: CreateUserInfoViewModel by viewModels()
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
binding = ActivityEtesyncEncryptionSettingsBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
ButterKnife.bind(this)
|
||||||
|
val intent = intent
|
||||||
|
caldavAccount = intent.getParcelableExtra(EXTRA_ACCOUNT)
|
||||||
|
userInfo = intent.getSerializableExtra(EXTRA_USER_INFO) as UserInfoManager.UserInfo
|
||||||
|
if (userInfo == null) {
|
||||||
|
binding.description.visibility = View.VISIBLE
|
||||||
|
binding.repeatEncryptionPasswordLayout.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
val toolbar = binding.toolbar.toolbar
|
||||||
|
toolbar.title = if (caldavAccount == null) getString(R.string.add_account) else caldavAccount!!.name
|
||||||
|
toolbar.navigationIcon = getDrawable(R.drawable.ic_outline_save_24px)
|
||||||
|
toolbar.setNavigationOnClickListener { save() }
|
||||||
|
toolbar.inflateMenu(R.menu.menu_help)
|
||||||
|
toolbar.setOnMenuItemClickListener(this)
|
||||||
|
themeColor.apply(toolbar)
|
||||||
|
createUserInfoViewModel.observe(this, this::returnDerivedKey, this::requestFailed)
|
||||||
|
if (createUserInfoViewModel.inProgress) {
|
||||||
|
showProgressIndicator()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showProgressIndicator() {
|
||||||
|
binding.progressBar.progressBar.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideProgressIndicator() {
|
||||||
|
binding.progressBar.progressBar.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun requestInProgress() = binding.progressBar.progressBar.visibility == View.VISIBLE
|
||||||
|
|
||||||
|
private fun returnDerivedKey(derivedKey: String) {
|
||||||
|
hideProgressIndicator()
|
||||||
|
val result = Intent()
|
||||||
|
result.putExtra(EXTRA_DERIVED_KEY, derivedKey)
|
||||||
|
setResult(Activity.RESULT_OK, result)
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun save() {
|
||||||
|
if (requestInProgress()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val encryptionPassword = newEncryptionPassword
|
||||||
|
val derivedKey = caldavAccount!!.getEncryptionPassword(encryption)
|
||||||
|
if (isNullOrEmpty(encryptionPassword) && isNullOrEmpty(derivedKey)) {
|
||||||
|
binding.encryptionPasswordLayout.error = getString(R.string.encryption_password_required)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (userInfo == null) {
|
||||||
|
val repeatEncryptionPassword = binding.repeatEncryptionPassword.text.toString().trim { it <= ' ' }
|
||||||
|
if (encryptionPassword != repeatEncryptionPassword) {
|
||||||
|
binding.repeatEncryptionPasswordLayout.error = getString(R.string.passwords_do_not_match)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val key = if (isNullOrEmpty(encryptionPassword)) derivedKey else deriveKey(caldavAccount!!.username!!, encryptionPassword)
|
||||||
|
val cryptoManager: CryptoManager
|
||||||
|
cryptoManager = try {
|
||||||
|
val version = if (userInfo == null) CURRENT_VERSION else userInfo!!.version!!.toInt()
|
||||||
|
CryptoManager(version, key, "userInfo")
|
||||||
|
} catch (e: VersionTooNewException) {
|
||||||
|
requestFailed(e)
|
||||||
|
return
|
||||||
|
} catch (e: IntegrityException) {
|
||||||
|
requestFailed(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (userInfo == null) {
|
||||||
|
showProgressIndicator()
|
||||||
|
createUserInfoViewModel.createUserInfo(caldavAccount!!, key)
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
userInfo!!.verify(cryptoManager)
|
||||||
|
returnDerivedKey(key)
|
||||||
|
} catch (e: IntegrityException) {
|
||||||
|
binding.encryptionPasswordLayout.error = getString(R.string.encryption_password_wrong)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun requestFailed(t: Throwable) {
|
||||||
|
hideProgressIndicator()
|
||||||
|
when (t) {
|
||||||
|
is HttpException -> showSnackbar(t.message)
|
||||||
|
is DisplayableException -> showSnackbar(t.resId)
|
||||||
|
is ConnectException -> showSnackbar(R.string.network_error)
|
||||||
|
else -> showSnackbar(R.string.error_adding_account, t.message!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showSnackbar(resId: Int, vararg formatArgs: Any) =
|
||||||
|
showSnackbar(getString(resId, *formatArgs))
|
||||||
|
|
||||||
|
private fun showSnackbar(message: String?) =
|
||||||
|
newSnackbar(message).show()
|
||||||
|
|
||||||
|
private fun newSnackbar(message: String?): Snackbar {
|
||||||
|
val snackbar = Snackbar.make(binding.rootLayout, message!!, 8000)
|
||||||
|
.setTextColor(getColor(R.color.snackbar_text_color))
|
||||||
|
.setActionTextColor(getColor(R.color.snackbar_action_color))
|
||||||
|
snackbar
|
||||||
|
.view
|
||||||
|
.setBackgroundColor(getColor(R.color.snackbar_background))
|
||||||
|
return snackbar
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnTextChanged(R.id.repeat_encryption_password)
|
||||||
|
fun onRpeatEncryptionPasswordChanged() {
|
||||||
|
binding.repeatEncryptionPasswordLayout.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnTextChanged(R.id.encryption_password)
|
||||||
|
fun onEncryptionPasswordChanged() {
|
||||||
|
binding.encryptionPasswordLayout.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private val newEncryptionPassword: String
|
||||||
|
get() = binding.encryptionPassword.text.toString().trim { it <= ' ' }
|
||||||
|
|
||||||
|
override fun finish() {
|
||||||
|
if (!requestInProgress()) {
|
||||||
|
super.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||||
|
return if (item.itemId == R.id.menu_help) {
|
||||||
|
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://tasks.org/etesync")))
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val EXTRA_USER_INFO = "extra_user_info"
|
||||||
|
const val EXTRA_ACCOUNT = "extra_account"
|
||||||
|
const val EXTRA_DERIVED_KEY = "extra_derived_key"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,48 +0,0 @@
|
|||||||
package org.tasks.etesync;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.tasks.caldav.BaseCaldavCalendarSettingsActivity;
|
|
||||||
import org.tasks.data.CaldavAccount;
|
|
||||||
import org.tasks.data.CaldavCalendar;
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
public class EteSyncCalendarSettingsActivity extends BaseCaldavCalendarSettingsActivity {
|
|
||||||
|
|
||||||
@Inject EteSyncClient client;
|
|
||||||
private CreateCalendarViewModel createCalendarViewModel;
|
|
||||||
private DeleteCalendarViewModel deleteCalendarViewModel;
|
|
||||||
private UpdateCalendarViewModel updateCalendarViewModel;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
ViewModelProvider provider = new ViewModelProvider(this);
|
|
||||||
createCalendarViewModel = provider.get(CreateCalendarViewModel.class);
|
|
||||||
deleteCalendarViewModel = provider.get(DeleteCalendarViewModel.class);
|
|
||||||
updateCalendarViewModel = provider.get(UpdateCalendarViewModel.class);
|
|
||||||
|
|
||||||
createCalendarViewModel.observe(this, this::createSuccessful, this::requestFailed);
|
|
||||||
deleteCalendarViewModel.observe(this, this::onDeleted, this::requestFailed);
|
|
||||||
updateCalendarViewModel.observe(this, uid -> updateCalendar(), this::requestFailed);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void createCalendar(CaldavAccount caldavAccount, String name, int color) {
|
|
||||||
createCalendarViewModel.createCalendar(client, caldavAccount, name, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void updateNameAndColor(CaldavAccount account, CaldavCalendar calendar, String name,
|
|
||||||
int color) {
|
|
||||||
updateCalendarViewModel.updateCalendar(client, account, calendar, name, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void deleteCalendar(CaldavAccount caldavAccount, CaldavCalendar caldavCalendar) {
|
|
||||||
deleteCalendarViewModel.deleteCalendar(client, caldavAccount, caldavCalendar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package org.tasks.etesync
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import org.tasks.caldav.BaseCaldavCalendarSettingsActivity
|
||||||
|
import org.tasks.data.CaldavAccount
|
||||||
|
import org.tasks.data.CaldavCalendar
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class EteSyncCalendarSettingsActivity : BaseCaldavCalendarSettingsActivity() {
|
||||||
|
private val createCalendarViewModel: CreateCalendarViewModel by viewModels()
|
||||||
|
private val deleteCalendarViewModel: DeleteCalendarViewModel by viewModels()
|
||||||
|
private val updateCalendarViewModel: UpdateCalendarViewModel by viewModels()
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
createCalendarViewModel.observe(this, this::createSuccessful, this::requestFailed)
|
||||||
|
deleteCalendarViewModel.observe(this, this::onDeleted, this::requestFailed)
|
||||||
|
updateCalendarViewModel.observe(this, { updateCalendar() }, this::requestFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createCalendar(caldavAccount: CaldavAccount, name: String, color: Int) =
|
||||||
|
createCalendarViewModel.createCalendar(caldavAccount, name, color)
|
||||||
|
|
||||||
|
override fun updateNameAndColor(
|
||||||
|
account: CaldavAccount, calendar: CaldavCalendar, name: String, color: Int) =
|
||||||
|
updateCalendarViewModel.updateCalendar(account, calendar, name, color)
|
||||||
|
|
||||||
|
override fun deleteCalendar(caldavAccount: CaldavAccount, caldavCalendar: CaldavCalendar) =
|
||||||
|
deleteCalendarViewModel.deleteCalendar(caldavAccount, caldavCalendar)
|
||||||
|
}
|
||||||
@ -1,16 +1,13 @@
|
|||||||
package org.tasks.etesync
|
package org.tasks.etesync
|
||||||
|
|
||||||
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
import org.tasks.data.CaldavAccount
|
import org.tasks.data.CaldavAccount
|
||||||
import org.tasks.data.CaldavCalendar
|
import org.tasks.data.CaldavCalendar
|
||||||
import org.tasks.ui.CompletableViewModel
|
import org.tasks.ui.CompletableViewModel
|
||||||
|
|
||||||
class UpdateCalendarViewModel : CompletableViewModel<String?>() {
|
class UpdateCalendarViewModel @ViewModelInject constructor(
|
||||||
fun updateCalendar(
|
private val client: EteSyncClient): CompletableViewModel<String?>() {
|
||||||
client: EteSyncClient,
|
fun updateCalendar(account: CaldavAccount, calendar: CaldavCalendar, name: String, color: Int) {
|
||||||
account: CaldavAccount?,
|
run { client.forAccount(account).updateCollection(calendar, name, color) }
|
||||||
calendar: CaldavCalendar?,
|
|
||||||
name: String?,
|
|
||||||
color: Int) {
|
|
||||||
run { client.forAccount(account!!).updateCollection(calendar!!, name, color) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,56 +0,0 @@
|
|||||||
package org.tasks.ui;
|
|
||||||
|
|
||||||
import static com.todoroo.andlib.utility.AndroidUtilities.assertMainThread;
|
|
||||||
|
|
||||||
import androidx.lifecycle.LifecycleOwner;
|
|
||||||
import androidx.lifecycle.MutableLiveData;
|
|
||||||
import androidx.lifecycle.Observer;
|
|
||||||
import androidx.lifecycle.ViewModel;
|
|
||||||
import io.reactivex.Completable;
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
|
||||||
import io.reactivex.functions.Action;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
|
||||||
|
|
||||||
@SuppressWarnings({"WeakerAccess", "RedundantSuppression"})
|
|
||||||
public class ActionViewModel extends ViewModel {
|
|
||||||
private final MutableLiveData<Boolean> completed = new MutableLiveData<>();
|
|
||||||
private final MutableLiveData<Throwable> error = new MutableLiveData<>();
|
|
||||||
private final CompositeDisposable disposables = new CompositeDisposable();
|
|
||||||
private boolean inProgress;
|
|
||||||
|
|
||||||
public void observe(
|
|
||||||
LifecycleOwner lifecycleOwner,
|
|
||||||
Observer<Boolean> completeObserver,
|
|
||||||
Observer<Throwable> errorObserver) {
|
|
||||||
completed.observe(lifecycleOwner, completeObserver);
|
|
||||||
error.observe(lifecycleOwner, errorObserver);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean inProgress() {
|
|
||||||
return inProgress;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void run(Action action) {
|
|
||||||
assertMainThread();
|
|
||||||
|
|
||||||
if (!inProgress) {
|
|
||||||
inProgress = true;
|
|
||||||
disposables.add(
|
|
||||||
Completable.fromAction(action)
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.doFinally(
|
|
||||||
() -> {
|
|
||||||
assertMainThread();
|
|
||||||
inProgress = false;
|
|
||||||
})
|
|
||||||
.subscribe(() -> completed.setValue(true), error::setValue));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCleared() {
|
|
||||||
disposables.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
package org.tasks.ui
|
||||||
|
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.observe
|
||||||
|
import com.todoroo.andlib.utility.AndroidUtilities
|
||||||
|
import io.reactivex.Completable
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
|
||||||
|
open class ActionViewModel : ViewModel() {
|
||||||
|
private val completed = MutableLiveData<Boolean>()
|
||||||
|
private val error = MutableLiveData<Throwable>()
|
||||||
|
private val disposables = CompositeDisposable()
|
||||||
|
|
||||||
|
var inProgress = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun observe(
|
||||||
|
lifecycleOwner: LifecycleOwner,
|
||||||
|
completeObserver: (Boolean) -> Unit,
|
||||||
|
errorObserver: (Throwable) -> Unit) {
|
||||||
|
completed.observe(lifecycleOwner, completeObserver)
|
||||||
|
error.observe(lifecycleOwner, errorObserver)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun run(action: () -> Unit) {
|
||||||
|
AndroidUtilities.assertMainThread()
|
||||||
|
if (!inProgress) {
|
||||||
|
inProgress = true
|
||||||
|
disposables.add(
|
||||||
|
Completable.fromAction(action)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.doFinally {
|
||||||
|
AndroidUtilities.assertMainThread()
|
||||||
|
inProgress = false
|
||||||
|
}
|
||||||
|
.subscribe({ completed.setValue(true) }) { value: Throwable -> error.setValue(value) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCleared() = disposables.clear()
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue