~williamvds/taskwarrior-android

db923ee835dd996117f95b0ac214107fcfb65cf6 — williamvds 4 years ago 28d74cd
Convert EditorActivity to Kotlin

Make AsyncTasks static to remove usage of bravo7.util.Tasks
3 files changed, 244 insertions(+), 276 deletions(-)

D app/src/main/java/kvj/taskw/ui/EditorActivity.java
A app/src/main/java/kvj/taskw/ui/EditorActivity.kt
M app/src/main/res/values/strings.xml
D app/src/main/java/kvj/taskw/ui/EditorActivity.java => app/src/main/java/kvj/taskw/ui/EditorActivity.java +0 -276
@@ 1,276 0,0 @@
package kvj.taskw.ui;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ProgressBar;

import timber.log.Timber;

import kvj.taskw.data.UUIDBundleAdapter;
import org.kvj.bravo7.form.BundleAdapter;
import org.kvj.bravo7.form.FormController;
import org.kvj.bravo7.form.impl.ViewFinder;
import org.kvj.bravo7.form.impl.bundle.StringBundleAdapter;
import org.kvj.bravo7.form.impl.widget.TransientAdapter;
import org.kvj.bravo7.util.DataUtil;
import org.kvj.bravo7.util.Tasks;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

import kvj.taskw.App;
import kvj.taskw.R;
import kvj.taskw.data.AccountController;
import kvj.taskw.data.Controller;
import kvj.taskw.data.UUIDBundleAdapter;

/**
 * Created by kvorobyev on 11/21/15.
 */
public class EditorActivity extends AppActivity {

    private Toolbar toolbar = null;
    private Editor editor = null;
    private FormController form = new FormController(new ViewFinder.ActivityViewFinder(this));
    Controller controller = App.controller();
    private List<String> priorities = null;
    private AccountController.TaskListener progressListener = null;
    private AccountController ac = null;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_editor);
        toolbar = findViewById(R.id.toolbar);
        editor = (Editor) getSupportFragmentManager().findFragmentById(R.id.editor_editor);
        ProgressBar progressBar = findViewById(R.id.progress);
        setSupportActionBar(toolbar);
        form.add(new TransientAdapter<>(new StringBundleAdapter(), null), App.KEY_ACCOUNT);
        form.add(new TransientAdapter<>(new UUIDBundleAdapter(), null), App.KEY_EDIT_UUID);
        form.add(new TransientAdapter<>(new BundleAdapter<Bundle>() {
            @Override
            public Bundle get(Bundle bundle, String name, Bundle def) {
                return bundle.getBundle(name);
            }

            @Override
            public void set(Bundle bundle, String name, Bundle value) {
                bundle.putBundle(name, value);
            }
        }, null).oneShot(), App.KEY_EDIT_DATA);
        form.add(new TransientAdapter<>(new BundleAdapter<ArrayList<String>>() {
            @Override
            public ArrayList<String> get(Bundle bundle, String name, ArrayList<String> def) {
                return bundle.getStringArrayList(name);
            }

            @Override
            public void set(Bundle bundle, String name, ArrayList<String> value) {
                bundle.putStringArrayList(name, value);
            }
        }, null).oneShot(), App.KEY_EDIT_DATA_FIELDS);
        editor.initForm(form);
        form.load(this, savedInstanceState);
        ac = controller.accountController(form);
        if (null == ac) {
            finish();
            controller.messageShort("Invalid arguments");
            return;
        }
        toolbar.setSubtitle(ac.name());
        progressListener = MainActivity.setupProgressListener(this, progressBar);
        new Tasks.ActivitySimpleTask<List<String>>(this){

            @Override
            protected List<String> doInBackground() {
                return ac.taskPriority();
            }

            @Override
            public void finish(List<String> result) {
                editor.setupPriorities(result);
                priorities = result;
                form.load(EditorActivity.this, savedInstanceState, App.KEY_EDIT_PRIORITY);
                editor.show(form);
                Bundle formData = form.getValue(App.KEY_EDIT_DATA);
                List<String> fields = form.getValue(App.KEY_EDIT_DATA_FIELDS);
                Timber.d("Edit: %s %s", formData, fields);
                if (null != formData && null != fields) { // Have data
                    for (String f : fields) { // $COMMENT
                        form.setValue(f, formData.getString(f));
                    }
                }
            }
        }.exec();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        form.save(outState);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_editor, menu);
        if (null != editor && !editor.adding(form)) { // New item mode
            menu.findItem(R.id.menu_tb_add_another).setVisible(false);
        }
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_tb_save:
                doSave(false);
                break;
            case R.id.menu_tb_add_another:
                doSave(true);
                break;
            case R.id.menu_tb_add_shortcut:
                createShortcut();
                break;
        }
        return true;
    }

    private void createShortcut() {
        Bundle bundle = new Bundle();
        form.save(bundle);
        bundle.remove(App.KEY_EDIT_UUID); // Just in case
        final Intent shortcutIntent = new Intent(this, EditorActivity.class);
        shortcutIntent.putExtras(bundle);
        shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        controller.input(this, "Shortcut name:", ac.name(), new DataUtil.Callback<CharSequence>() {

            @Override
            public boolean call(CharSequence value) {
                controller.createShortcut(shortcutIntent, value.toString().trim());
                return true;
            }
        }, null);
    }

    @Override
    protected void onResume() {
        super.onResume();
        ac.listeners().add(progressListener, true);
    }

    @Override
    protected void onPause() {
        super.onPause();
        ac.listeners().remove(progressListener);
    }

    @Override
    public void onBackPressed() {
        if (!form.changed()) { // No changes - just close
            super.onBackPressed();
            return;
        }
        Timber.d("Changed: %s", form.changes());
        controller.question(this, "There are some changes, discard?", new Runnable() {

            @Override
            public void run() {
                EditorActivity.super.onBackPressed();
            }
        }, null);
    }
    
    private String propertyChange(String key, String modifier) {
        String value = form.getValue(key);
        if (TextUtils.isEmpty(value)) {
            value = "";
        }
        return String.format("%s:%s", modifier, value);
    }

    private String save() {
        if (!form.changed()) { // No change - no save
            return "Nothing has been changed";
        }
        String description = form.getValue(App.KEY_EDIT_DESCRIPTION);
        if (TextUtils.isEmpty(description)) { // Empty desc
            return "Description is mandatory";
        }
        List<String> changes = new ArrayList<>();
        for (String key : form.changes()) { // Make changes
            if (App.KEY_EDIT_DESCRIPTION.equals(key)) { // Direct
                changes.add(AccountController.escape(description));
            }
            if (App.KEY_EDIT_PROJECT.equals(key)) { // Direct
                changes.add(propertyChange(key, "project"));
            }
            if (App.KEY_EDIT_DUE.equals(key)) { // Direct
                changes.add(propertyChange(key, "due"));
            }
            if (App.KEY_EDIT_SCHEDULED.equals(key)) { // Direct
                changes.add(propertyChange(key, "scheduled"));
            }
            if (App.KEY_EDIT_WAIT.equals(key)) { // Direct
                changes.add(propertyChange(key, "wait"));
            }
            if (App.KEY_EDIT_UNTIL.equals(key)) { // Direct
                changes.add(propertyChange(key, "until"));
            }
            if (App.KEY_EDIT_RECUR.equals(key)) { // Direct
                changes.add(propertyChange(key, "recur"));
            }
            if (App.KEY_EDIT_PRIORITY.equals(key)) { // Direct
                changes.add(String.format("priority:%s", priorities
                    .get(form.getValue(App.KEY_EDIT_PRIORITY, Integer.class))));
            }
            if (App.KEY_EDIT_TAGS.equals(key)) { // Direct
                List<String> tags = new ArrayList<>();
                String tagsStr = form.getValue(App.KEY_EDIT_TAGS);
                Collections.addAll(tags, tagsStr.split(" "));
                changes.add(String.format("tags:%s", MainListAdapter.join(",", tags)));
            }
        }
        UUID uuid = form.getValue(App.KEY_EDIT_UUID);
        boolean completed = form.getValue(App.KEY_EDIT_STATUS, Integer.class) > 0;
        Timber.d("Saving change: %s %s %s", uuid, changes, completed);
        if (uuid == null) { // Add new
            return completed? ac.taskLog(changes): ac.taskAdd(changes);
        } else {
            return ac.taskModify(uuid, changes);
        }
    }

    private void doSave(final boolean addAnother) {
        new Tasks.ActivitySimpleTask<String>(this) {

            @Override
            protected String doInBackground() {
                return save();
            }

            @Override
            public void finish(String result) {
                if (!TextUtils.isEmpty(result)) { // Failed
                    controller.messageLong(result);
                } else {
                    controller.messageShort("Task added");
                    EditorActivity.this.setResult(Activity.RESULT_OK);
                    if (addAnother) { // Keep everything except description
                        form.setValue(App.KEY_EDIT_DESCRIPTION, "");
                        form.getView(App.KEY_EDIT_DESCRIPTION).requestFocus();
                    } else { // Finish activity
                        EditorActivity.this.finish();
                    }
                }
            }
        }.exec();
    }

}

A app/src/main/java/kvj/taskw/ui/EditorActivity.kt => app/src/main/java/kvj/taskw/ui/EditorActivity.kt +242 -0
@@ 0,0 1,242 @@
package kvj.taskw.ui

import java.util.ArrayList
import java.util.UUID

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.text.TextUtils
import android.view.Menu
import android.view.MenuItem
import android.view.View

import timber.log.Timber

import org.kvj.bravo7.form.BundleAdapter
import org.kvj.bravo7.form.FormController
import org.kvj.bravo7.form.impl.ViewFinder
import org.kvj.bravo7.form.impl.bundle.StringBundleAdapter
import org.kvj.bravo7.form.impl.widget.TransientAdapter
import org.kvj.bravo7.util.DataUtil

import kvj.taskw.App
import kvj.taskw.R
import kvj.taskw.data.AccountController
import kvj.taskw.data.Controller
import kvj.taskw.data.UUIDBundleAdapter

import kotlinx.android.synthetic.main.activity_editor.*

class EditorActivity : AppActivity() {
    private val form = FormController(ViewFinder.ActivityViewFinder(this))
    internal var controller = App.controller<Controller>()
    private var priorities = listOf<String>()
    private var progressListener: AccountController.TaskListener? = null
    private var ac: AccountController? = null
    private var editor: Editor? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_editor)
        setSupportActionBar(toolbar)

        form.apply {
            add<Any, String>(TransientAdapter(StringBundleAdapter(), null), App.KEY_ACCOUNT)
            add<Any, UUID>  (TransientAdapter(UUIDBundleAdapter(),   null), App.KEY_EDIT_UUID)

            add<Any, Bundle?>(TransientAdapter(object : BundleAdapter<Bundle?>() {
                override fun get(bundle: Bundle?, name: String, def: Bundle?) = bundle?.getBundle(name)
                override fun set(bundle: Bundle?, name: String, value: Bundle?) { bundle?.putBundle(name, value) }
            }, null).oneShot(), App.KEY_EDIT_DATA)

            add<Any, ArrayList<String>>(TransientAdapter(object : BundleAdapter<ArrayList<String>>() {
                override fun get(bundle: Bundle, name: String, def: ArrayList<String>?) = bundle.getStringArrayList(name)
                override fun set(bundle: Bundle, name: String, value: ArrayList<String>?) { bundle.putStringArrayList(name, value) }
            }, null).oneShot(), App.KEY_EDIT_DATA_FIELDS)
        }

        editor = supportFragmentManager.findFragmentById(R.id.editor_editor) as Editor
        editor!!.initForm(form)
        form.load(this@EditorActivity, savedInstanceState)

        ac = controller.accountController(form)
        if (ac == null) {
            finish()
            controller.messageShort("Invalid arguments")
            return
        }

        toolbar.subtitle = ac!!.name()
        progressListener = MainActivity.setupProgressListener(this, progress)
        GetPrioritiesTask(this, savedInstanceState).execute()
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        form.save(outState)
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.menu_editor, menu)

        val uuid = form.getValue<UUID>(App.KEY_EDIT_UUID)
        menu.findItem(R.id.menu_tb_add_another).isVisible = (uuid != null)

        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.menu_tb_save ->         doSave(false)
            R.id.menu_tb_add_another ->  doSave(true)
            R.id.menu_tb_add_shortcut -> createShortcut()
        }

        return true
    }

    override fun onResume() {
        super.onResume()
        ac?.listeners()?.add(progressListener, true)
    }

    override fun onPause() {
        super.onPause()
        ac?.listeners()?.remove(progressListener)
    }

    override fun onBackPressed() {
        if (!form.changed()) { // No changes - just close
            super.onBackPressed()
            return
        }

        Timber.d("Changed: %s", form.changes())
        controller.question(this,
            "There are some changes, discard?",
            Runnable { super@EditorActivity.onBackPressed() },
            null)
    }

    private fun createShortcut() {
        val ac = ac
        ac ?: return

        val bundle = Bundle()
        form.save(bundle)
        bundle.remove(App.KEY_EDIT_UUID) // Just in case

        val shortcutIntent = Intent(this, EditorActivity::class.java).apply {
            putExtras(bundle)
            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        }

        val callback = DataUtil.Callback<CharSequence> { value ->
            controller.createShortcut(shortcutIntent, value.toString().trim { it <= ' ' })
            true
        }

        controller.input(this, "Shortcut name:", ac.name(), callback, null)
    }

    private fun propertyChange(key: String, modifier: String): String {
        var value = form.getValue<String>(key)
        if (value.isBlank()) value = ""

        return String.format("%s:%s", modifier, value)
    }

    private fun save(): String? {
        if (!form.changed()) return "Nothing has been changed"

        val description = form.getValue<String>(App.KEY_EDIT_DESCRIPTION)
        if (TextUtils.isEmpty(description)) return "Description is mandatory"

        val uuid      = form.getValue<UUID>(App.KEY_EDIT_UUID)
        val completed = form.getValue(App.KEY_EDIT_STATUS,   Int::class.java) > 0
        val priority  = form.getValue(App.KEY_EDIT_PRIORITY, Int::class.java)

        val changes = ArrayList<String>()
        form.changes().forEach { key ->
            when (key) {
                App.KEY_EDIT_DESCRIPTION ->  changes.add(AccountController.escape(description))
                App.KEY_EDIT_PROJECT ->      changes.add(propertyChange(key, "project"))
                App.KEY_EDIT_DUE ->          changes.add(propertyChange(key, "due"))
                App.KEY_EDIT_SCHEDULED ->    changes.add(propertyChange(key, "scheduled"))
                App.KEY_EDIT_WAIT ->         changes.add(propertyChange(key, "wait"))
                App.KEY_EDIT_UNTIL ->        changes.add(propertyChange(key, "until"))
                App.KEY_EDIT_RECUR ->        changes.add(propertyChange(key, "recur"))
                App.KEY_EDIT_PRIORITY ->     changes.add("priority:${priorities[priority]}")
                App.KEY_EDIT_TAGS -> {
                    val tagsStr = form.getValue<String>(App.KEY_EDIT_TAGS)
                    val tags = tagsStr.split("([,;]\\s*|\\s+)".toRegex())
                    changes.add(String.format("tags:%s", tags.joinToString(",")))
                }
            }
        }

        Timber.d("Saving change: %s %s %s", uuid, changes, completed)

        uuid?.let { return ac!!.taskModify(uuid, changes) }
        return if (completed) ac!!.taskLog(changes) else ac!!.taskAdd(changes)
    }

    private fun doSave(addAnother: Boolean) {
        SaveTask(this, addAnother).execute()
    }

    companion object {
        private class GetPrioritiesTask(activity: EditorActivity,
                                        val bundle: Bundle?)
            : StaticAsyncTask<EditorActivity, Void, Void, List<String>>(activity) {

            override fun EditorActivity.background(vararg params: Void): List<String> {
                return ac!!.taskPriority()
            }

            override fun EditorActivity.finish(result: List<String>) {
                priorities = result
                editor!!.setupPriorities(priorities)
                form.load(this, bundle, App.KEY_EDIT_PRIORITY)
                editor!!.show(form)

                val formData = form.getValue<Bundle>(App.KEY_EDIT_DATA)
                val fields   = form.getValue<List<String>>(App.KEY_EDIT_DATA_FIELDS)

                Timber.d("Edit: %s %s", formData, fields)

                if (formData == null || fields == null) return
                fields.forEach { form.setValue(it, formData.getString(it)) }
            }
        }

        private class SaveTask(context: EditorActivity,
                               val addAnother: Boolean)
            : StaticAsyncTask<EditorActivity, Void, Void, String?>(context) {

            override fun EditorActivity.background(vararg params: Void) = save()

            override fun EditorActivity.finish(result: String?) {
                if (!result.isNullOrBlank()) {
                    controller.messageLong(result)
                    return
                }

                val uuid = form.getValue<UUID>(App.KEY_EDIT_UUID)
                controller.messageShort(
                    getString(if (uuid != null) R.string.edit_task_success else R.string.add_task_success))

                setResult(Activity.RESULT_OK)

                if (!addAnother) {
                    finish()
                    return
                }

                form.setValue(App.KEY_EDIT_DESCRIPTION, "")
                form.getView<View>(App.KEY_EDIT_DESCRIPTION).requestFocus()
            }
        }
    }
}

M app/src/main/res/values/strings.xml => app/src/main/res/values/strings.xml +2 -0
@@ 88,6 88,8 @@

    <!-- Task editor -->
    <string name="add_another">Add another</string>
    <string name="edit_task_success">Task edited</string>
    <string name="add_task_success">Task added</string>

    <!-- Custom command dialog -->
    <string name="command_prompt_hint">Arguments</string>