~williamvds/taskwarrior-android

47e60c13755879cb6dc91a33a971d4b881b1715a — williamvds 4 years ago c11c8de
Convert TextEditor to AppForm
4 files changed, 119 insertions(+), 159 deletions(-)

M app/src/main/java/kvj/taskw/ui/AppForm.kt
D app/src/main/java/kvj/taskw/ui/TextEditor.java
A app/src/main/java/kvj/taskw/ui/TextEditor.kt
M app/src/main/res/values/strings.xml
M app/src/main/java/kvj/taskw/ui/AppForm.kt => app/src/main/java/kvj/taskw/ui/AppForm.kt +3 -0
@@ 29,6 29,9 @@ abstract class AppForm<T: FormData>: AppActivity() {
        saveToBundle(outState)
    }

    protected val initialized: Boolean
        get() = ::data.isInitialized

    private fun loadFromBundle(bundle: Bundle?): Boolean {
        val stored = bundle?.getParcelable<T?>(App.KEY_EDIT_DATA)


D app/src/main/java/kvj/taskw/ui/TextEditor.java => app/src/main/java/kvj/taskw/ui/TextEditor.java +0 -159
@@ 1,159 0,0 @@
package kvj.taskw.ui;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;

import timber.log.Timber;

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.TextViewCharSequenceAdapter;
import org.kvj.bravo7.form.impl.widget.TransientAdapter;
import org.kvj.bravo7.util.Tasks;

import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.Scanner;

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

/**
 * Created by vorobyev on 11/30/15.
 */
public class TextEditor extends AppActivity {

    FormController form = new FormController(new ViewFinder.ActivityViewFinder(this));
    private Toolbar toolbar = null;
    Controller controller = App.controller();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_text_editor);
        toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        form.add(new TextViewCharSequenceAdapter(R.id.text_editor_input, null).trackOrigin(true), App.KEY_TEXT_INPUT);
        form.add(new TransientAdapter<>(new StringBundleAdapter(), null), App.KEY_TEXT_TARGET);
        form.load(this, savedInstanceState);
        if (null == form.getValue(App.KEY_TEXT_TARGET)) {
            // No data - load from Uri
            loadText(getIntent());
        } else {
            updateToolbar();
        }
    }

    private void updateToolbar() {
        toolbar.setSubtitle(form.getValue(App.KEY_TEXT_TARGET, String.class));
    }

    private void loadText(Intent intent) {
        final Uri uri = intent.getData();
        if (uri == null) return;

        new Tasks.ActivitySimpleTask<String>(this) {

            @Override
            protected String doInBackground() {
                try {
                    InputStream stream = getContentResolver().openInputStream(uri);
                    Scanner s = new Scanner(stream).useDelimiter("\\A");
                    return s.hasNext() ? s.next() : "";
                } catch (Exception ex) {
                    return null;
                }
            }

            @Override
            public void finish(String result) {
                Timber.d("File loaded: %s %s", uri, result != null);
                if (null == result) {
                    controller.messageLong("File IO error");
                    TextEditor.this.finish();
                } else {
                    form.setValue(App.KEY_TEXT_TARGET, uri.toString(), true);
                    form.setValue(App.KEY_TEXT_INPUT, result, true);
                    updateToolbar();
                }
            }
        }.exec();
    }

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

    @Override
    public void onBackPressed() {
        if (!form.changed()) {
            super.onBackPressed(); // Close
            return;
        }
        Timber.d("Changes: %s", form.changes());
        controller.question(this, "There are some changes, discard?", new Runnable() {
            @Override
            public void run() {
                finish();
            }
        }, null);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_text_editor, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_tb_save:
                save();
                break;
        }
        return true;
    }

    private void save() {
        if (!form.changed()) finish();

        final Uri uri = Uri.parse((String) form.getValue(App.KEY_TEXT_TARGET));
        final String text = form.getValue(App.KEY_TEXT_INPUT);
        new Tasks.ActivitySimpleTask<Boolean>(this) {

            @Override
            protected Boolean doInBackground() {
                try {
                    OutputStreamWriter stream = new OutputStreamWriter(getContentResolver().openOutputStream(uri));
                    stream.write(text);
                    stream.close();
                    return true;
                } catch (Exception ex) {
                    return false;
                }
            }

            @Override
            public void finish(Boolean result) {
                if (!result) {
                    // Failure
                    controller.messageLong("File write failure");
                } else {
                    controller.messageShort("File saved");
                    setResult(RESULT_OK);
                    TextEditor.this.finish();
                }
            }
        }.exec();
    }

}

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

import java.io.OutputStreamWriter
import java.util.Scanner

import android.app.Activity
import android.net.Uri
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem

import timber.log.Timber

import kvj.taskw.R

import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.activity_editor.*
import kotlinx.android.synthetic.main.activity_text_editor.text_editor_input

class TextEditor : AppForm<TextEditor.Form>() {
    override val layout = R.layout.activity_text_editor

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setSupportActionBar(toolbar)

        if (!initialized) {
            data = Form(intent.data)
            LoadTask(this).execute()
        }
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.menu_text_editor, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.menu_tb_save -> submit()
        }
        return true
    }

    override fun submit() {
        super.submit()
        SaveTask(this).execute()
    }

    override fun loadFromForm() {
        text_editor_input.setText(data.text)
        toolbar.subtitle = data.uri?.path.toString()
    }

    override fun saveToForm() {
        data.text = text_editor_input.text.toString()
    }

    override fun hasChanges() = !text_editor_input.text.isNullOrBlank()

    @Parcelize
    data class Form @JvmOverloads constructor(
            var uri: Uri? = null,
            var text: String? = null
    ) : FormData

    companion object {
        private class LoadTask(activity: TextEditor)
            : StaticAsyncTask<TextEditor, Void, Void, String?>(activity) {

            override fun TextEditor.background(vararg params: Void) = try {
                val stream = contentResolver.openInputStream(data.uri!!)
                val s = Scanner(stream).useDelimiter("\\A")
                if (s.hasNext()) s.next() else ""
            } catch (ex: Exception) {
                Timber.e(ex, "Failed to read file")
                null
            }

            override fun TextEditor.finish(result: String?) {
                if (result != null) {
                    Timber.d("File loaded: %s", data.uri)
                    data.text = result
                    loadFromForm()
                } else {
                    controller.messageLong(getString(R.string.error_read_failed))
                    finish()
                }
            }
        }

        private class SaveTask(activity: TextEditor)
            : StaticAsyncTask<TextEditor, Void, Void, Boolean>(activity) {
            override fun TextEditor.background(vararg params: Void) = try {
                val stream = OutputStreamWriter(contentResolver.openOutputStream(data.uri!!))
                stream.write(data.text)
                stream.close()
                true
            } catch (ex: Exception) {
                Timber.e(ex, "Failed to save file")
                false
            }

            override fun TextEditor.finish(result: Boolean) {
                if (result) {
                    setResult(Activity.RESULT_OK)
                    finish()
                    return
                } else
                    controller.messageLong(getString(R.string.error_save_failed))
            }
        }
    }
}

M app/src/main/res/values/strings.xml => app/src/main/res/values/strings.xml +2 -0
@@ 85,6 85,8 @@
    <!-- Text editor -->
    <string name="text_editor_title">Taskwarrior text editor</string>
    <string name="text_editor_hint">Text editor</string>
    <string name="error_save_failed">Failed to save file</string>
    <string name="error_read_failed">Failed to read file</string>

    <!-- Task editor -->
    <string name="add_another">Add another</string>