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
|
||||
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
import com.google.api.services.tasks.model.TaskList
|
||||
import com.todoroo.astrid.gtasks.api.GtasksInvoker
|
||||
import org.tasks.ui.CompletableViewModel
|
||||
|
||||
class CreateListViewModel : CompletableViewModel<TaskList?>() {
|
||||
fun createList(invoker: GtasksInvoker, account: String?, name: String?) {
|
||||
run { invoker.forAccount(account!!).createGtaskList(name) }
|
||||
class CreateListViewModel @ViewModelInject constructor(
|
||||
private val invoker: GtasksInvoker) : CompletableViewModel<TaskList>() {
|
||||
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
|
||||
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
import com.google.api.services.tasks.model.TaskList
|
||||
import com.todoroo.astrid.gtasks.api.GtasksInvoker
|
||||
import org.tasks.data.GoogleTaskList
|
||||
import org.tasks.ui.CompletableViewModel
|
||||
|
||||
class RenameListViewModel : CompletableViewModel<TaskList?>() {
|
||||
fun renameList(invoker: GtasksInvoker, list: GoogleTaskList, name: String?) {
|
||||
run { invoker.forAccount(list.account!!).renameGtaskList(list.remoteId, name) }
|
||||
class RenameListViewModel @ViewModelInject constructor(
|
||||
private val invoker: GtasksInvoker) : CompletableViewModel<TaskList>() {
|
||||
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
|
||||
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
import org.tasks.ui.CompletableViewModel
|
||||
|
||||
class AddCaldavAccountViewModel : CompletableViewModel<String>() {
|
||||
fun addAccount(client: CaldavClient, url: String?, username: String?, password: String?) {
|
||||
class AddCaldavAccountViewModel @ViewModelInject constructor(
|
||||
private val client: CaldavClient) : CompletableViewModel<String>() {
|
||||
fun addAccount(url: String, username: String, password: String) {
|
||||
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
|
||||
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
import org.tasks.data.CaldavAccount
|
||||
import org.tasks.ui.CompletableViewModel
|
||||
|
||||
class CreateCalendarViewModel : CompletableViewModel<String?>() {
|
||||
fun createCalendar(client: CaldavClient, account: CaldavAccount?, name: String?, color: Int) {
|
||||
class CreateCalendarViewModel @ViewModelInject constructor(
|
||||
private val client: CaldavClient): CompletableViewModel<String?>() {
|
||||
fun createCalendar(account: CaldavAccount, name: String, color: Int) {
|
||||
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
|
||||
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
import org.tasks.ui.CompletableViewModel
|
||||
|
||||
class UpdateCaldavAccountViewModel : CompletableViewModel<String>() {
|
||||
fun updateCaldavAccount(
|
||||
client: CaldavClient, url: String?, username: String?, password: String?) {
|
||||
run {
|
||||
client.forUrl(url, username, password).homeSet
|
||||
}
|
||||
class UpdateCaldavAccountViewModel @ViewModelInject constructor(
|
||||
private val client: CaldavClient) : CompletableViewModel<String>() {
|
||||
fun updateCaldavAccount(url: String, username: String, password: String?) {
|
||||
run { client.forUrl(url, username, password).homeSet }
|
||||
}
|
||||
}
|
@ -1,12 +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.CompletableViewModel
|
||||
|
||||
class UpdateCalendarViewModel : CompletableViewModel<String?>() {
|
||||
fun updateCalendar(
|
||||
client: CaldavClient, account: CaldavAccount?, calendar: CaldavCalendar?, name: String?, color: Int) {
|
||||
class UpdateCalendarViewModel @ViewModelInject constructor(
|
||||
private val client: CaldavClient) : CompletableViewModel<String?>() {
|
||||
fun updateCalendar(account: CaldavAccount, calendar: CaldavCalendar, name: String, color: Int) {
|
||||
run { client.forCalendar(account, calendar).updateCollection(name, color) }
|
||||
}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
package org.tasks.etesync
|
||||
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
import org.tasks.data.CaldavAccount
|
||||
import org.tasks.ui.CompletableViewModel
|
||||
|
||||
class CreateCalendarViewModel : CompletableViewModel<String?>() {
|
||||
fun createCalendar(client: EteSyncClient, account: CaldavAccount?, name: String?, color: Int) {
|
||||
run { client.forAccount(account!!).makeCollection(name, color) }
|
||||
class CreateCalendarViewModel @ViewModelInject constructor(
|
||||
private val client: EteSyncClient) : CompletableViewModel<String?>() {
|
||||
fun createCalendar(account: CaldavAccount, name: String, color: Int) {
|
||||
run { client.forAccount(account).makeCollection(name, color) }
|
||||
}
|
||||
}
|
@ -1,12 +1,14 @@
|
||||
package org.tasks.etesync
|
||||
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
import org.tasks.data.CaldavAccount
|
||||
import org.tasks.ui.CompletableViewModel
|
||||
|
||||
class CreateUserInfoViewModel : CompletableViewModel<String?>() {
|
||||
fun createUserInfo(client: EteSyncClient, caldavAccount: CaldavAccount?, derivedKey: String?) {
|
||||
class CreateUserInfoViewModel @ViewModelInject constructor(
|
||||
private val client: EteSyncClient): CompletableViewModel<String>() {
|
||||
fun createUserInfo(caldavAccount: CaldavAccount, derivedKey: String) {
|
||||
run {
|
||||
client.forAccount(caldavAccount!!).createUserInfo(derivedKey)
|
||||
client.forAccount(caldavAccount).createUserInfo(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
|
||||
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
import org.tasks.data.CaldavAccount
|
||||
import org.tasks.data.CaldavCalendar
|
||||
import org.tasks.ui.CompletableViewModel
|
||||
|
||||
class UpdateCalendarViewModel : CompletableViewModel<String?>() {
|
||||
fun updateCalendar(
|
||||
client: EteSyncClient,
|
||||
account: CaldavAccount?,
|
||||
calendar: CaldavCalendar?,
|
||||
name: String?,
|
||||
color: Int) {
|
||||
run { client.forAccount(account!!).updateCollection(calendar!!, name, color) }
|
||||
class UpdateCalendarViewModel @ViewModelInject constructor(
|
||||
private val client: EteSyncClient): CompletableViewModel<String?>() {
|
||||
fun updateCalendar(account: CaldavAccount, 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