diff --git a/app/src/main/java/org/tasks/activities/BaseListSettingsActivity.java b/app/src/main/java/org/tasks/activities/BaseListSettingsActivity.java deleted file mode 100644 index 214c83391..000000000 --- a/app/src/main/java/org/tasks/activities/BaseListSettingsActivity.java +++ /dev/null @@ -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)); - } -} diff --git a/app/src/main/java/org/tasks/activities/BaseListSettingsActivity.kt b/app/src/main/java/org/tasks/activities/BaseListSettingsActivity.kt new file mode 100644 index 000000000..861608fb7 --- /dev/null +++ b/app/src/main/java/org/tasks/activities/BaseListSettingsActivity.kt @@ -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" + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/activities/CreateListViewModel.kt b/app/src/main/java/org/tasks/activities/CreateListViewModel.kt index 54278421d..ced9d0769 100644 --- a/app/src/main/java/org/tasks/activities/CreateListViewModel.kt +++ b/app/src/main/java/org/tasks/activities/CreateListViewModel.kt @@ -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() { - fun createList(invoker: GtasksInvoker, account: String?, name: String?) { - run { invoker.forAccount(account!!).createGtaskList(name) } +class CreateListViewModel @ViewModelInject constructor( + private val invoker: GtasksInvoker) : CompletableViewModel() { + fun createList(account: String, name: String) { + run { invoker.forAccount(account).createGtaskList(name)!! } } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/activities/DeleteListViewModel.java b/app/src/main/java/org/tasks/activities/DeleteListViewModel.java deleted file mode 100644 index 606a2f22e..000000000 --- a/app/src/main/java/org/tasks/activities/DeleteListViewModel.java +++ /dev/null @@ -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())); - } -} diff --git a/app/src/main/java/org/tasks/activities/DeleteListViewModel.kt b/app/src/main/java/org/tasks/activities/DeleteListViewModel.kt new file mode 100644 index 000000000..dbade9183 --- /dev/null +++ b/app/src/main/java/org/tasks/activities/DeleteListViewModel.kt @@ -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) } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/activities/FilterSettingsActivity.kt b/app/src/main/java/org/tasks/activities/FilterSettingsActivity.kt index 64f303cd4..e9543f498 100644 --- a/app/src/main/java/org/tasks/activities/FilterSettingsActivity.kt +++ b/app/src/main/java/org/tasks/activities/FilterSettingsActivity.kt @@ -213,13 +213,11 @@ class FilterSettingsActivity : BaseListSettingsActivity() { outState.putString(EXTRA_CRITERIA, CriterionInstance.serialize(criteria)) } - override fun isNew(): Boolean { - return filter == null - } + override val isNew: Boolean + get() = filter == null - override fun getToolbarTitle(): String { - return if (isNew) getString(R.string.FLA_new_filter) else filter!!.listingTitle - } + override val toolbarTitle: String? + get() = if (isNew) getString(R.string.FLA_new_filter) else filter!!.listingTitle @OnTextChanged(R.id.name) fun onTextChanged() { @@ -276,9 +274,8 @@ class FilterSettingsActivity : BaseListSettingsActivity() { super.finish() } - override fun getLayout(): Int { - return R.layout.filter_settings_activity - } + override val layout: Int + get() = R.layout.filter_settings_activity override fun delete() { filterDao.delete(filter!!.id) diff --git a/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.java b/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.java deleted file mode 100644 index ab5407695..000000000 --- a/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.java +++ /dev/null @@ -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; - } -} diff --git a/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.kt b/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.kt new file mode 100644 index 000000000..2b665bca2 --- /dev/null +++ b/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.kt @@ -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(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" + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/activities/PlaceSettingsActivity.kt b/app/src/main/java/org/tasks/activities/PlaceSettingsActivity.kt index fdc2c753e..bb2fdde14 100644 --- a/app/src/main/java/org/tasks/activities/PlaceSettingsActivity.kt +++ b/app/src/main/java/org/tasks/activities/PlaceSettingsActivity.kt @@ -59,7 +59,8 @@ class PlaceSettingsActivity : BaseListSettingsActivity(), MapFragment.MapFragmen updateTheme() } - override fun getLayout() = R.layout.activity_location_settings + override val layout: Int + get() = R.layout.activity_location_settings override fun hasChanges() = name.text.toString() != place.displayName || selectedColor != place.color @@ -89,11 +90,11 @@ class PlaceSettingsActivity : BaseListSettingsActivity(), MapFragment.MapFragmen finish() } - override fun isNew() = false + override val isNew: Boolean + get() = false - override fun getToolbarTitle(): String? { - return place.address ?: place.displayName - } + override val toolbarTitle: String? + get() = place.address ?: place.displayName override fun delete() { locationDao.deleteGeofencesByPlace(place.uid!!) diff --git a/app/src/main/java/org/tasks/activities/RenameListViewModel.kt b/app/src/main/java/org/tasks/activities/RenameListViewModel.kt index 120e37350..6e09b81ab 100644 --- a/app/src/main/java/org/tasks/activities/RenameListViewModel.kt +++ b/app/src/main/java/org/tasks/activities/RenameListViewModel.kt @@ -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() { - 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() { + fun renameList(list: GoogleTaskList, name: String) { + run { invoker.forAccount(list.account!!).renameGtaskList(list.remoteId, name)!! } } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/activities/TagSettingsActivity.java b/app/src/main/java/org/tasks/activities/TagSettingsActivity.java deleted file mode 100644 index eed682d0f..000000000 --- a/app/src/main/java/org/tasks/activities/TagSettingsActivity.java +++ /dev/null @@ -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(); - } -} diff --git a/app/src/main/java/org/tasks/activities/TagSettingsActivity.kt b/app/src/main/java/org/tasks/activities/TagSettingsActivity.kt new file mode 100644 index 000000000..9d9feec57 --- /dev/null +++ b/app/src/main/java/org/tasks/activities/TagSettingsActivity.kt @@ -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$ + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/caldav/AddCaldavAccountViewModel.kt b/app/src/main/java/org/tasks/caldav/AddCaldavAccountViewModel.kt index e29d7a594..71fd8736d 100644 --- a/app/src/main/java/org/tasks/caldav/AddCaldavAccountViewModel.kt +++ b/app/src/main/java/org/tasks/caldav/AddCaldavAccountViewModel.kt @@ -1,9 +1,11 @@ package org.tasks.caldav +import androidx.hilt.lifecycle.ViewModelInject import org.tasks.ui.CompletableViewModel -class AddCaldavAccountViewModel : CompletableViewModel() { - fun addAccount(client: CaldavClient, url: String?, username: String?, password: String?) { +class AddCaldavAccountViewModel @ViewModelInject constructor( + private val client: CaldavClient) : CompletableViewModel() { + fun addAccount(url: String, username: String, password: String) { run { client.setForeground().forUrl(url, username, password).homeSet } } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/caldav/BaseCaldavAccountSettingsActivity.kt b/app/src/main/java/org/tasks/caldav/BaseCaldavAccountSettingsActivity.kt index 0b11ca055..f771341f9 100644 --- a/app/src/main/java/org/tasks/caldav/BaseCaldavAccountSettingsActivity.kt +++ b/app/src/main/java/org/tasks/caldav/BaseCaldavAccountSettingsActivity.kt @@ -220,7 +220,7 @@ abstract class BaseCaldavAccountSettingsActivity : ThemedInjectingAppCompatActiv failed -> return caldavAccount == null -> { showProgressIndicator() - addAccount(url, username, password) + addAccount(url, username, password!!) } needsValidation() -> { showProgressIndicator() @@ -235,10 +235,11 @@ abstract class BaseCaldavAccountSettingsActivity : ThemedInjectingAppCompatActiv } } - protected abstract fun addAccount(url: String?, username: String?, password: String?) - protected abstract fun updateAccount(url: String?, username: String?, password: String?) + protected abstract fun addAccount(url: String, username: String, password: String) + protected abstract fun updateAccount(url: String, username: String, password: String?) protected abstract fun updateAccount() protected abstract val helpUrl: String? + protected fun requestFailed(t: Throwable) { hideProgressIndicator() if (t is HttpException) { diff --git a/app/src/main/java/org/tasks/caldav/BaseCaldavCalendarSettingsActivity.java b/app/src/main/java/org/tasks/caldav/BaseCaldavCalendarSettingsActivity.java deleted file mode 100644 index 01bf39144..000000000 --- a/app/src/main/java/org/tasks/caldav/BaseCaldavCalendarSettingsActivity.java +++ /dev/null @@ -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(); - } - } -} diff --git a/app/src/main/java/org/tasks/caldav/BaseCaldavCalendarSettingsActivity.kt b/app/src/main/java/org/tasks/caldav/BaseCaldavCalendarSettingsActivity.kt new file mode 100644 index 000000000..bba813695 --- /dev/null +++ b/app/src/main/java/org/tasks/caldav/BaseCaldavCalendarSettingsActivity.kt @@ -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" + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.kt b/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.kt index 792686820..4ee3fd244 100644 --- a/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.kt +++ b/app/src/main/java/org/tasks/caldav/CaldavAccountSettingsActivity.kt @@ -9,19 +9,16 @@ import dagger.hilt.android.AndroidEntryPoint import org.tasks.R import org.tasks.data.CaldavAccount import timber.log.Timber -import javax.inject.Inject @AndroidEntryPoint class CaldavAccountSettingsActivity : BaseCaldavAccountSettingsActivity(), Toolbar.OnMenuItemClickListener { - @Inject lateinit var client: CaldavClient - private val addCaldavAccountViewModel: AddCaldavAccountViewModel by viewModels() private val updateCaldavAccountViewModel: UpdateCaldavAccountViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - addCaldavAccountViewModel.observe(this, { addAccount(it) }, { requestFailed(it) }) - updateCaldavAccountViewModel.observe(this, { updateAccount(it) }, { requestFailed(it) }) + addCaldavAccountViewModel.observe(this, this::addAccount, this::requestFailed) + updateCaldavAccountViewModel.observe(this, this::updateAccount, this::requestFailed) } override val description: Int @@ -56,12 +53,12 @@ class CaldavAccountSettingsActivity : BaseCaldavAccountSettingsActivity(), Toolb finish() } - override fun addAccount(url: String?, username: String?, password: String?) { - addCaldavAccountViewModel.addAccount(client, url, username, password) + override fun addAccount(url: String, username: String, password: String) { + addCaldavAccountViewModel.addAccount(url, username, password) } - override fun updateAccount(url: String?, username: String?, password: String?) { - updateCaldavAccountViewModel.updateCaldavAccount(client, url, username, password) + override fun updateAccount(url: String, username: String, password: String?) { + updateCaldavAccountViewModel.updateCaldavAccount(url, username, password) } override fun updateAccount() { diff --git a/app/src/main/java/org/tasks/caldav/CaldavCalendarSettingsActivity.java b/app/src/main/java/org/tasks/caldav/CaldavCalendarSettingsActivity.java deleted file mode 100644 index 65a0eb7b9..000000000 --- a/app/src/main/java/org/tasks/caldav/CaldavCalendarSettingsActivity.java +++ /dev/null @@ -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); - } -} diff --git a/app/src/main/java/org/tasks/caldav/CaldavCalendarSettingsActivity.kt b/app/src/main/java/org/tasks/caldav/CaldavCalendarSettingsActivity.kt new file mode 100644 index 000000000..75229efc0 --- /dev/null +++ b/app/src/main/java/org/tasks/caldav/CaldavCalendarSettingsActivity.kt @@ -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) +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/caldav/CreateCalendarViewModel.kt b/app/src/main/java/org/tasks/caldav/CreateCalendarViewModel.kt index a41f8cf1f..778b83a29 100644 --- a/app/src/main/java/org/tasks/caldav/CreateCalendarViewModel.kt +++ b/app/src/main/java/org/tasks/caldav/CreateCalendarViewModel.kt @@ -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() { - fun createCalendar(client: CaldavClient, account: CaldavAccount?, name: String?, color: Int) { +class CreateCalendarViewModel @ViewModelInject constructor( + private val client: CaldavClient): CompletableViewModel() { + fun createCalendar(account: CaldavAccount, name: String, color: Int) { run { client.forAccount(account).makeCollection(name, color) } } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/caldav/DeleteCalendarViewModel.java b/app/src/main/java/org/tasks/caldav/DeleteCalendarViewModel.java deleted file mode 100644 index 6894d9526..000000000 --- a/app/src/main/java/org/tasks/caldav/DeleteCalendarViewModel.java +++ /dev/null @@ -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()); - } -} diff --git a/app/src/main/java/org/tasks/caldav/DeleteCalendarViewModel.kt b/app/src/main/java/org/tasks/caldav/DeleteCalendarViewModel.kt new file mode 100644 index 000000000..f7de26816 --- /dev/null +++ b/app/src/main/java/org/tasks/caldav/DeleteCalendarViewModel.kt @@ -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() } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/caldav/LocalListSettingsActivity.kt b/app/src/main/java/org/tasks/caldav/LocalListSettingsActivity.kt index a79bb6ebf..03ed4dfb0 100644 --- a/app/src/main/java/org/tasks/caldav/LocalListSettingsActivity.kt +++ b/app/src/main/java/org/tasks/caldav/LocalListSettingsActivity.kt @@ -10,12 +10,14 @@ import org.tasks.data.CaldavDao @AndroidEntryPoint class LocalListSettingsActivity : BaseCaldavCalendarSettingsActivity() { - override fun getLayout() = R.layout.activity_caldav_calendar_settings + override val layout: Int + get() = R.layout.activity_caldav_calendar_settings override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - toolbar.menu.findItem(R.id.delete)?.isVisible = caldavDao.getCalendarsByAccount(CaldavDao.LOCAL).size > 1 + toolbar.menu.findItem(R.id.delete)?.isVisible = + caldavDao.getCalendarsByAccount(CaldavDao.LOCAL).size > 1 } override fun createCalendar(caldavAccount: CaldavAccount, name: String, color: Int) = diff --git a/app/src/main/java/org/tasks/caldav/UpdateCaldavAccountViewModel.kt b/app/src/main/java/org/tasks/caldav/UpdateCaldavAccountViewModel.kt index b42c55904..5358243ab 100644 --- a/app/src/main/java/org/tasks/caldav/UpdateCaldavAccountViewModel.kt +++ b/app/src/main/java/org/tasks/caldav/UpdateCaldavAccountViewModel.kt @@ -1,12 +1,11 @@ package org.tasks.caldav +import androidx.hilt.lifecycle.ViewModelInject import org.tasks.ui.CompletableViewModel -class UpdateCaldavAccountViewModel : CompletableViewModel() { - 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() { + fun updateCaldavAccount(url: String, username: String, password: String?) { + run { client.forUrl(url, username, password).homeSet } } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/caldav/UpdateCalendarViewModel.kt b/app/src/main/java/org/tasks/caldav/UpdateCalendarViewModel.kt index 3d515b7ec..6e6563de5 100644 --- a/app/src/main/java/org/tasks/caldav/UpdateCalendarViewModel.kt +++ b/app/src/main/java/org/tasks/caldav/UpdateCalendarViewModel.kt @@ -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() { - fun updateCalendar( - client: CaldavClient, account: CaldavAccount?, calendar: CaldavCalendar?, name: String?, color: Int) { +class UpdateCalendarViewModel @ViewModelInject constructor( + private val client: CaldavClient) : CompletableViewModel() { + fun updateCalendar(account: CaldavAccount, calendar: CaldavCalendar, name: String, color: Int) { run { client.forCalendar(account, calendar).updateCollection(name, color) } } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/etesync/AddEteSyncAccountViewModel.kt b/app/src/main/java/org/tasks/etesync/AddEteSyncAccountViewModel.kt index dc412776f..e5155bae4 100644 --- a/app/src/main/java/org/tasks/etesync/AddEteSyncAccountViewModel.kt +++ b/app/src/main/java/org/tasks/etesync/AddEteSyncAccountViewModel.kt @@ -1,11 +1,14 @@ package org.tasks.etesync import androidx.core.util.Pair +import androidx.hilt.lifecycle.ViewModelInject import com.etesync.journalmanager.UserInfoManager +import com.etesync.journalmanager.UserInfoManager.UserInfo import org.tasks.ui.CompletableViewModel -class AddEteSyncAccountViewModel : CompletableViewModel>() { - fun addAccount(client: EteSyncClient, url: String?, username: String?, password: String?) { +class AddEteSyncAccountViewModel @ViewModelInject constructor( + private val client: EteSyncClient): CompletableViewModel>() { + fun addAccount(url: String, username: String, password: String) { run { client.setForeground() val token = client.forUrl(url, username, null, null).getToken(password) diff --git a/app/src/main/java/org/tasks/etesync/CreateCalendarViewModel.kt b/app/src/main/java/org/tasks/etesync/CreateCalendarViewModel.kt index 613c79e77..4ee2ef8e5 100644 --- a/app/src/main/java/org/tasks/etesync/CreateCalendarViewModel.kt +++ b/app/src/main/java/org/tasks/etesync/CreateCalendarViewModel.kt @@ -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() { - 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() { + fun createCalendar(account: CaldavAccount, name: String, color: Int) { + run { client.forAccount(account).makeCollection(name, color) } } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/etesync/CreateUserInfoViewModel.kt b/app/src/main/java/org/tasks/etesync/CreateUserInfoViewModel.kt index 066d234d1..549448c3a 100644 --- a/app/src/main/java/org/tasks/etesync/CreateUserInfoViewModel.kt +++ b/app/src/main/java/org/tasks/etesync/CreateUserInfoViewModel.kt @@ -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() { - fun createUserInfo(client: EteSyncClient, caldavAccount: CaldavAccount?, derivedKey: String?) { +class CreateUserInfoViewModel @ViewModelInject constructor( + private val client: EteSyncClient): CompletableViewModel() { + fun createUserInfo(caldavAccount: CaldavAccount, derivedKey: String) { run { - client.forAccount(caldavAccount!!).createUserInfo(derivedKey) + client.forAccount(caldavAccount).createUserInfo(derivedKey) derivedKey } } diff --git a/app/src/main/java/org/tasks/etesync/DeleteCalendarViewModel.java b/app/src/main/java/org/tasks/etesync/DeleteCalendarViewModel.java deleted file mode 100644 index 62e79903c..000000000 --- a/app/src/main/java/org/tasks/etesync/DeleteCalendarViewModel.java +++ /dev/null @@ -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)); - } -} diff --git a/app/src/main/java/org/tasks/etesync/DeleteCalendarViewModel.kt b/app/src/main/java/org/tasks/etesync/DeleteCalendarViewModel.kt new file mode 100644 index 000000000..61a9d7e4f --- /dev/null +++ b/app/src/main/java/org/tasks/etesync/DeleteCalendarViewModel.kt @@ -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) } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/etesync/EncryptionSettingsActivity.java b/app/src/main/java/org/tasks/etesync/EncryptionSettingsActivity.java deleted file mode 100644 index 50935ca6a..000000000 --- a/app/src/main/java/org/tasks/etesync/EncryptionSettingsActivity.java +++ /dev/null @@ -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); - } - } -} diff --git a/app/src/main/java/org/tasks/etesync/EncryptionSettingsActivity.kt b/app/src/main/java/org/tasks/etesync/EncryptionSettingsActivity.kt new file mode 100644 index 000000000..b43a520ee --- /dev/null +++ b/app/src/main/java/org/tasks/etesync/EncryptionSettingsActivity.kt @@ -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" + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/etesync/EteSyncAccountSettingsActivity.kt b/app/src/main/java/org/tasks/etesync/EteSyncAccountSettingsActivity.kt index 55f0777c6..6c71ed502 100644 --- a/app/src/main/java/org/tasks/etesync/EteSyncAccountSettingsActivity.kt +++ b/app/src/main/java/org/tasks/etesync/EteSyncAccountSettingsActivity.kt @@ -41,8 +41,8 @@ class EteSyncAccountSettingsActivity : BaseCaldavAccountSettingsActivity(), Tool override fun onResume() { super.onResume() if (!isFinishing) { - addAccountViewModel.observe(this, { addAccount(it) }, { requestFailed(it) }) - updateAccountViewModel.observe(this, { updateAccount(it) }, { requestFailed(it) }) + addAccountViewModel.observe(this, this::addAccount, this::requestFailed) + updateAccountViewModel.observe(this, this::updateAccount, this::requestFailed) } } @@ -116,13 +116,12 @@ class EteSyncAccountSettingsActivity : BaseCaldavAccountSettingsActivity(), Tool return super.needsValidation() || isNullOrEmpty(caldavAccount!!.encryptionKey) } - override fun addAccount(url: String?, username: String?, password: String?) { - addAccountViewModel.addAccount(eteSyncClient, url, username, password) + override fun addAccount(url: String, username: String, password: String) { + addAccountViewModel.addAccount(url, username, password) } - override fun updateAccount(url: String?, username: String?, password: String?) { + override fun updateAccount(url: String, username: String, password: String?) { updateAccountViewModel.updateAccount( - eteSyncClient, url, username, if (PASSWORD_MASK == password) null else password, diff --git a/app/src/main/java/org/tasks/etesync/EteSyncCalendarSettingsActivity.java b/app/src/main/java/org/tasks/etesync/EteSyncCalendarSettingsActivity.java deleted file mode 100644 index bf16620f3..000000000 --- a/app/src/main/java/org/tasks/etesync/EteSyncCalendarSettingsActivity.java +++ /dev/null @@ -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); - } -} diff --git a/app/src/main/java/org/tasks/etesync/EteSyncCalendarSettingsActivity.kt b/app/src/main/java/org/tasks/etesync/EteSyncCalendarSettingsActivity.kt new file mode 100644 index 000000000..7c89d6c88 --- /dev/null +++ b/app/src/main/java/org/tasks/etesync/EteSyncCalendarSettingsActivity.kt @@ -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) +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/etesync/UpdateCalendarViewModel.kt b/app/src/main/java/org/tasks/etesync/UpdateCalendarViewModel.kt index 8c3cd928b..27d9f7374 100644 --- a/app/src/main/java/org/tasks/etesync/UpdateCalendarViewModel.kt +++ b/app/src/main/java/org/tasks/etesync/UpdateCalendarViewModel.kt @@ -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() { - 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() { + fun updateCalendar(account: CaldavAccount, calendar: CaldavCalendar, name: String, color: Int) { + run { client.forAccount(account).updateCollection(calendar, name, color) } } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/etesync/UpdateEteSyncAccountViewModel.kt b/app/src/main/java/org/tasks/etesync/UpdateEteSyncAccountViewModel.kt index f76972d03..b0867c156 100644 --- a/app/src/main/java/org/tasks/etesync/UpdateEteSyncAccountViewModel.kt +++ b/app/src/main/java/org/tasks/etesync/UpdateEteSyncAccountViewModel.kt @@ -1,13 +1,14 @@ package org.tasks.etesync import androidx.core.util.Pair -import com.etesync.journalmanager.UserInfoManager +import androidx.hilt.lifecycle.ViewModelInject +import com.etesync.journalmanager.UserInfoManager.UserInfo import org.tasks.Strings.isNullOrEmpty import org.tasks.ui.CompletableViewModel -class UpdateEteSyncAccountViewModel : CompletableViewModel>() { - fun updateAccount( - client: EteSyncClient, url: String?, user: String?, pass: String?, token: String?) { +class UpdateEteSyncAccountViewModel @ViewModelInject constructor( + private val client: EteSyncClient) : CompletableViewModel>() { + fun updateAccount(url: String, user: String, pass: String?, token: String) { run { client.setForeground() if (isNullOrEmpty(pass)) { diff --git a/app/src/main/java/org/tasks/ui/ActionViewModel.java b/app/src/main/java/org/tasks/ui/ActionViewModel.java deleted file mode 100644 index 4d5216da4..000000000 --- a/app/src/main/java/org/tasks/ui/ActionViewModel.java +++ /dev/null @@ -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 completed = new MutableLiveData<>(); - private final MutableLiveData error = new MutableLiveData<>(); - private final CompositeDisposable disposables = new CompositeDisposable(); - private boolean inProgress; - - public void observe( - LifecycleOwner lifecycleOwner, - Observer completeObserver, - Observer 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(); - } -} diff --git a/app/src/main/java/org/tasks/ui/ActionViewModel.kt b/app/src/main/java/org/tasks/ui/ActionViewModel.kt new file mode 100644 index 000000000..c286ee1e6 --- /dev/null +++ b/app/src/main/java/org/tasks/ui/ActionViewModel.kt @@ -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() + private val error = MutableLiveData() + 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() +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/ui/CompletableViewModel.kt b/app/src/main/java/org/tasks/ui/CompletableViewModel.kt index c4bf8a26d..062044b47 100644 --- a/app/src/main/java/org/tasks/ui/CompletableViewModel.kt +++ b/app/src/main/java/org/tasks/ui/CompletableViewModel.kt @@ -14,7 +14,9 @@ abstract class CompletableViewModel : ViewModel() { private val data = MutableLiveData() private val error = MutableLiveData() private val disposables = CompositeDisposable() - private var inProgress = false + + var inProgress = false + private set fun observe( lifecycleOwner: LifecycleOwner, @@ -24,10 +26,6 @@ abstract class CompletableViewModel : ViewModel() { error.observe(lifecycleOwner, errorObserver) } - fun inProgress(): Boolean { - return inProgress - } - protected fun run(callable: () -> T) { AndroidUtilities.assertMainThread() if (!inProgress) { @@ -44,9 +42,7 @@ abstract class CompletableViewModel : ViewModel() { } } - override fun onCleared() { - disposables.dispose() - } + override fun onCleared() = disposables.dispose() fun removeObserver(owner: LifecycleOwner) { data.removeObservers(owner)