M app/src/main/AndroidManifest.xml => app/src/main/AndroidManifest.xml +1 -2
@@ 62,8 62,7 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
- <activity android:name=".ui.RunActivity">
- </activity>
+ <activity android:name=".ui.RunActivity" />
<activity android:name=".ui.TagDialog" />
M app/src/main/java/kvj/taskw/data/AccountController.java => app/src/main/java/kvj/taskw/data/AccountController.java +2 -9
@@ 45,7 45,6 @@ import timber.log.Timber;
import kvj.taskw.App;
import kvj.taskw.sync.SSLHelper;
import kvj.taskw.ui.MainListAdapter;
-import kvj.taskw.ui.RunActivity;
import kvj.taskw.notifications.NotificationFactory;
import kvj.taskw.notifications.NotificationChannels;
import kvj.taskw.ui.StaticAsyncTask;
@@ 255,7 254,7 @@ public class AccountController {
new LoadNotificationsTask(this).execute();
}
- private class StringAggregator implements StreamConsumer {
+ public static class StringAggregator implements StreamConsumer {
StringBuilder builder = new StringBuilder();
@@ 267,7 266,7 @@ public class AccountController {
builder.append(line);
}
- private String text() {
+ public String text() {
return builder.toString();
}
}
@@ 900,12 899,6 @@ public class AccountController {
return query.replace(" ", "\\ "); //.replace("(", "\\(").replace(")", "\\)");
}
- public Intent intentForRunTask() {
- Intent intent = new Intent(controller.context(), RunActivity.class);
- intent.putExtra(App.KEY_ACCOUNT, id);
- return intent;
- }
-
public boolean intentForEditor(Intent intent, UUID uuid) {
intent.putExtra(App.KEY_ACCOUNT, id);
List<String> priorities = taskPriority();
M app/src/main/java/kvj/taskw/ui/MainActivity.java => app/src/main/java/kvj/taskw/ui/MainActivity.java +1 -1
@@ 214,7 214,7 @@ public class MainActivity extends AppActivity implements Controller.ToastMessage
refreshAccount(account);
break;
case R.id.menu_nav_run:
- startActivity(ac.intentForRunTask());
+ RunActivity.start(this, new RunActivity.Form(account));
break;
case R.id.menu_nav_debug:
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
D app/src/main/java/kvj/taskw/ui/RunActivity.java => app/src/main/java/kvj/taskw/ui/RunActivity.java +0 -266
@@ 1,266 0,0 @@
-package kvj.taskw.ui;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.ShareActionProvider;
-import android.support.v7.widget.Toolbar;
-import android.text.TextUtils;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.EditText;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import timber.log.Timber;
-
-import org.jetbrains.annotations.NotNull;
-import org.kvj.bravo7.form.FormController;
-import org.kvj.bravo7.form.impl.ViewFinder;
-import org.kvj.bravo7.form.impl.bundle.ListStringBundleAdapter;
-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 java.util.ArrayList;
-import java.util.List;
-
-import kvj.taskw.App;
-import kvj.taskw.R;
-import kvj.taskw.data.AccountController;
-import kvj.taskw.data.Controller;
-
-/**
- * Created by vorobyev on 12/1/15.
- */
-public class RunActivity extends AppActivity {
-
- FormController form = new FormController(new ViewFinder.ActivityViewFinder(this));
- Controller controller = App.controller();
- private AccountController ac = null;
- private RunAdapter adapter = null;
- private kvj.taskw.data.AccountController.TaskListener progressListener = null;
- private ShareActionProvider mShareActionProvider = null;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_run);
- Toolbar toolbar = findViewById(R.id.toolbar);
- RecyclerView list = findViewById(R.id.run_output);
- list.setLayoutManager(new LinearLayoutManager(this));
- setSupportActionBar(toolbar);
- final EditText input = findViewById(R.id.run_command);
- input.setOnKeyListener(new View.OnKeyListener() {
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (keyCode != KeyEvent.KEYCODE_ENTER) return false;
-
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- run();
- }
-
- return true;
- }
- });
- form.add(new TransientAdapter<>(new StringBundleAdapter(), null), App.KEY_ACCOUNT);
- form.add(new TransientAdapter<>(new ListStringBundleAdapter(), null), App.KEY_RUN_OUTPUT);
- form.add(new TextViewCharSequenceAdapter(R.id.run_command, null), App.KEY_RUN_COMMAND);
- form.load(this, savedInstanceState);
- progressListener = MainActivity
- .setupProgressListener(this, (ProgressBar) findViewById(R.id.progress));
- ac = controller.accountController(form);
- if (null == ac) {
- controller.messageShort("Invalid arguments");
- finish();
- return;
- }
- adapter = new RunAdapter(form.getValue(App.KEY_RUN_OUTPUT, ArrayList.class));
- list.setAdapter(adapter);
- toolbar.setSubtitle(ac.name());
- }
-
- private void shareAll() {
- CharSequence text = adapter.allText();
- if (TextUtils.isEmpty(text)) { // Error
- controller.messageShort("Nothing to share");
- return;
- }
- Intent sendIntent = new Intent();
- sendIntent.setAction(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_TEXT, text);
- sendIntent.setType("text/plain");
- if (null != mShareActionProvider) {
- Timber.d("Share provider set");
- mShareActionProvider.setShareIntent(sendIntent);
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_run, menu);
-
- // Locate MenuItem with ShareActionProvider
- MenuItem item = menu.findItem(R.id.menu_tb_run_share);
-
- // Fetch and store ShareActionProvider
- mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(item);
-
- // Return true to display menu
- return true;
-
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_tb_run_run:
- run();
- return true;
- case R.id.menu_tb_run_copy:
- copyAll();
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- private void copyAll() {
- CharSequence text = adapter.allText();
- if (TextUtils.isEmpty(text)) { // Error
- controller.messageShort("Nothing to copy");
- return;
- }
- controller.copyToClipboard(text);
- }
-
- private static class RunTask extends StaticAsyncTask<RunActivity, Void, Void, Boolean> {
- private String input;
- private AccountController.ListAggregator out, err;
-
- RunTask(RunActivity activity, String input, AccountController.ListAggregator out, AccountController.ListAggregator err) {
- super(activity);
- this.input = input;
- this.out = out;
- this.err = err;
- }
-
- @Override
- protected Boolean background(RunActivity activity, Void... params) {
- int result = activity.ac.taskCustom(input, out, err);
- return 0 == result;
- }
-
- @Override
- protected void finish(RunActivity activity, Boolean result) {
- out.data().addAll(err.data());
- activity.adapter.addAll(out.data());
- activity.shareAll();
- }
- }
-
- private void run() {
- final String input = form.getValue(App.KEY_RUN_COMMAND);
- if (TextUtils.isEmpty(input)) {
- controller.messageShort("Input is empty");
- return;
- }
- adapter.clear();
- final AccountController.ListAggregator out = new AccountController.ListAggregator();
- final AccountController.ListAggregator err = new AccountController.ListAggregator();
- new RunTask(this, input, out, err).execute();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- if (null != adapter) {
- form.setValue(App.KEY_RUN_OUTPUT, adapter.data);
- }
- form.save(outState);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- ac.listeners().add(progressListener, true);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- ac.listeners().remove(progressListener);
- }
-
- class RunAdapter extends RecyclerView.Adapter<RunAdapter.RunAdapterItem> {
-
- ArrayList<String> data = new ArrayList<>();
-
- public RunAdapter(ArrayList<String> data) {
- if (null != data) {
- this.data.addAll(data);
- }
- }
-
- @NotNull
- @Override
- public RunAdapterItem onCreateViewHolder(@NotNull ViewGroup parent, int viewType) {
- return new RunAdapterItem(LayoutInflater.from(parent.getContext())
- .inflate(R.layout.item_run_output, parent, false));
- }
-
- @Override
- public void onBindViewHolder(@NotNull RunAdapterItem holder, int position) {
- holder.text.setText(data.get(position));
- }
-
- @Override
- public int getItemCount() {
- return data.size();
- }
-
- void clear() {
- notifyItemRangeRemoved(0, data.size());
- data.clear();
- }
-
- public synchronized void addAll(List<String> data) {
- int from = getItemCount();
- this.data.addAll(data);
- notifyItemRangeInserted(from, data.size());
- }
-
- public CharSequence allText() {
- StringBuilder sb = new StringBuilder();
- for (String line : data) { // Copy to
- if (sb.length() > 0) {
- sb.append('\n');
- }
- sb.append(line);
- }
- return sb;
- }
-
- class RunAdapterItem extends RecyclerView.ViewHolder implements View.OnLongClickListener {
-
- private final TextView text;
-
- public RunAdapterItem(View itemView) {
- super(itemView);
- itemView.setOnLongClickListener(this);
- this.text = itemView.findViewById(R.id.run_item_text);
- }
-
- @Override
- public boolean onLongClick(View v) {
- controller.copyToClipboard(data.get(getAdapterPosition()));
- return true;
- }
- }
- }
-
-}
A app/src/main/java/kvj/taskw/ui/RunActivity.kt => app/src/main/java/kvj/taskw/ui/RunActivity.kt +148 -0
@@ 0,0 1,148 @@
+package kvj.taskw.ui
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import android.support.v4.view.MenuItemCompat
+import android.support.v7.widget.ShareActionProvider
+import android.view.KeyEvent
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+
+import timber.log.Timber
+
+import kvj.taskw.App
+import kvj.taskw.R
+import kvj.taskw.data.AccountController
+import kvj.taskw.data.AccountController.TaskListener
+
+import kotlinx.android.parcel.Parcelize
+import kotlinx.android.synthetic.main.activity_run.*
+
+class RunActivity : AppForm<RunActivity.Form>() {
+ override val layout = R.layout.activity_run
+
+ private lateinit var ac: AccountController
+ private lateinit var progressListener: TaskListener
+ private lateinit var shareProvider: ShareActionProvider
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setSupportActionBar(toolbar)
+
+ input.setOnKeyListener(View.OnKeyListener { _, keyCode, event ->
+ if (keyCode != KeyEvent.KEYCODE_ENTER) return@OnKeyListener false
+ if (event.action == KeyEvent.ACTION_DOWN) submit()
+ true
+ })
+
+ progressListener = MainActivity.setupProgressListener(this, progress)
+ }
+
+ override fun onResume() {
+ super.onResume()
+ ac.listeners().add(progressListener, true)
+ }
+
+ override fun onPause() {
+ super.onPause()
+ ac.listeners().remove(progressListener)
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(R.menu.menu_run, menu)
+
+ val item = menu.findItem(R.id.menu_tb_run_share)
+ shareProvider = MenuItemCompat.getActionProvider(item) as ShareActionProvider
+
+ return true
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
+ R.id.menu_tb_run_run -> {
+ submit()
+ return true
+ }
+ R.id.menu_tb_run_copy -> {
+ copyAll()
+ return true
+ }
+ }
+
+ return super.onOptionsItemSelected(item)
+ }
+
+ override fun submit() {
+ super.submit()
+ RunTask(this).execute()
+ }
+
+ override fun loadFromForm() {
+ input.setText(data.input)
+ output.text = data.output
+
+ ac = controller.accountController(data.account)
+ toolbar.subtitle = ac.name()
+ setShareIntent()
+ }
+
+ override fun saveToForm() {
+ data.input = input.text.toString()
+ data.output = output.text.toString()
+ }
+
+ override fun hasChanges() = false
+
+ private fun setShareIntent() {
+ if (!::shareProvider.isInitialized) {
+ Timber.e("Share provider not initialized!")
+ return
+ }
+
+ shareProvider.setShareIntent(Intent().apply {
+ action = Intent.ACTION_SEND
+ putExtra(Intent.EXTRA_TEXT, output.text)
+ type = "text/plain"
+ })
+ }
+
+ private fun copyAll() {
+ controller.copyToClipboard(output.text)
+ }
+
+ @Parcelize
+ data class Form @JvmOverloads constructor(
+ val account: String,
+ var input: String? = null,
+ var output: String? = null
+ ) : FormData
+
+ companion object {
+ @JvmStatic
+ fun start(activity: Activity, data: Form) {
+ val intent = Intent(activity, RunActivity::class.java).apply {
+ putExtra(App.KEY_EDIT_DATA, data)
+ }
+
+ activity.startActivity(intent)
+ }
+
+ private class RunTask(activity: RunActivity)
+ : StaticAsyncTask<RunActivity, Void, Void, Boolean>(activity) {
+ private val out = AccountController.StringAggregator()
+ private val err = AccountController.StringAggregator()
+
+ override fun RunActivity.background(vararg params: Void): Boolean {
+ val result = ac.taskCustom(data.input, out, err)
+ return result == 0
+ }
+
+ override fun RunActivity.finish(result: Boolean) {
+ output.text = getString(R.string.command_result_format, out.text(), err.text())
+ setShareIntent()
+ }
+ }
+ }
+}
M app/src/main/res/layout/activity_run.xml => app/src/main/res/layout/activity_run.xml +8 -4
@@ 34,9 34,10 @@
android:layout_marginTop="6dp">
<EditText
- android:id="@+id/run_command"
+ android:id="@+id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
android:hint="@string/command_prompt_hint"
android:imeOptions="actionSend"
android:importantForAutofill="no"
@@ 48,12 49,15 @@
</android.support.design.widget.TextInputLayout>
</LinearLayout>
- <android.support.v7.widget.RecyclerView
- android:id="@+id/run_output"
+ <TextView
+ android:id="@+id/output"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
- android:scrollbars="vertical" />
+ android:fontFamily="monospace"
+ android:singleLine="false"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textIsSelectable="true" />
</LinearLayout>
<ProgressBar
M app/src/main/res/values/strings.xml => app/src/main/res/values/strings.xml +1 -0
@@ 100,6 100,7 @@
<!-- Custom command dialog -->
<string name="command_prompt_hint">Arguments</string>
<string name="run">Run</string>
+ <string name="command_result_format">%1$s\n\nErrors:\n%2$s</string>
<!-- New account dialog -->
<string name="account_name_hint">Name</string>