diff --git a/android/BOINC/res/drawable-hdpi/ic_menu_add.png b/android/BOINC/res/drawable-hdpi/ic_menu_add.png new file mode 100644 index 0000000000..444e8a5ee8 Binary files /dev/null and b/android/BOINC/res/drawable-hdpi/ic_menu_add.png differ diff --git a/android/BOINC/res/drawable-ldpi/ic_menu_add.png b/android/BOINC/res/drawable-ldpi/ic_menu_add.png new file mode 100644 index 0000000000..89620af8c0 Binary files /dev/null and b/android/BOINC/res/drawable-ldpi/ic_menu_add.png differ diff --git a/android/BOINC/res/drawable-mdpi/ic_menu_add.png b/android/BOINC/res/drawable-mdpi/ic_menu_add.png new file mode 100644 index 0000000000..361c7c460e Binary files /dev/null and b/android/BOINC/res/drawable-mdpi/ic_menu_add.png differ diff --git a/android/BOINC/res/drawable-xhdpi/ic_menu_add.png b/android/BOINC/res/drawable-xhdpi/ic_menu_add.png new file mode 100644 index 0000000000..7d498a96e2 Binary files /dev/null and b/android/BOINC/res/drawable-xhdpi/ic_menu_add.png differ diff --git a/android/BOINC/res/drawable/abortw24.png b/android/BOINC/res/drawable/abortw24.png new file mode 100644 index 0000000000..7dffc3f616 Binary files /dev/null and b/android/BOINC/res/drawable/abortw24.png differ diff --git a/android/BOINC/res/drawable/pausew24.png b/android/BOINC/res/drawable/pausew24.png new file mode 100644 index 0000000000..bbc8b81a13 Binary files /dev/null and b/android/BOINC/res/drawable/pausew24.png differ diff --git a/android/BOINC/res/layout/prefs_layout_loading.xml b/android/BOINC/res/layout/prefs_layout_loading.xml new file mode 100644 index 0000000000..92a5effad9 --- /dev/null +++ b/android/BOINC/res/layout/prefs_layout_loading.xml @@ -0,0 +1,40 @@ + + + + + + + + + diff --git a/android/BOINC/res/layout/tasks_layout_listitem.xml b/android/BOINC/res/layout/tasks_layout_listitem.xml index a75a61567d..471714baa6 100644 --- a/android/BOINC/res/layout/tasks_layout_listitem.xml +++ b/android/BOINC/res/layout/tasks_layout_listitem.xml @@ -73,6 +73,8 @@ - + diff --git a/android/BOINC/res/layout/tasks_layout_listitem_expansion.xml b/android/BOINC/res/layout/tasks_layout_listitem_expansion.xml new file mode 100644 index 0000000000..876fc3b593 --- /dev/null +++ b/android/BOINC/res/layout/tasks_layout_listitem_expansion.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/android/BOINC/res/values/strings.xml b/android/BOINC/res/values/strings.xml index 4b605a7b7c..d30ebbefaa 100644 --- a/android/BOINC/res/values/strings.xml +++ b/android/BOINC/res/values/strings.xml @@ -197,6 +197,12 @@ paused stopping stopping + + + Abort task + Abort task: + Abort + Cancel Reading transfers… diff --git a/android/BOINC/src/edu/berkeley/boinc/AppPreferences.java b/android/BOINC/src/edu/berkeley/boinc/AppPreferences.java index ce72bae2df..daad27aa04 100644 --- a/android/BOINC/src/edu/berkeley/boinc/AppPreferences.java +++ b/android/BOINC/src/edu/berkeley/boinc/AppPreferences.java @@ -18,7 +18,6 @@ ******************************************************************************/ package edu.berkeley.boinc; -import edu.berkeley.boinc.client.ClientNotification; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; diff --git a/android/BOINC/src/edu/berkeley/boinc/ConfirmationDialog.java b/android/BOINC/src/edu/berkeley/boinc/ConfirmationDialog.java new file mode 100644 index 0000000000..e0eb9f2b5d --- /dev/null +++ b/android/BOINC/src/edu/berkeley/boinc/ConfirmationDialog.java @@ -0,0 +1,50 @@ +package edu.berkeley.boinc; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.content.DialogInterface.OnClickListener; + +public class ConfirmationDialog extends android.support.v4.app.DialogFragment { + private OnClickListener mConfirmClickListener = null; + + public static ConfirmationDialog newInstance(String title, String message, String confirm) { + ConfirmationDialog frag = new ConfirmationDialog(); + Bundle args = new Bundle(); + args.putString("title", title); + args.putString("message", message); + args.putString("confirm", confirm); + frag.setArguments(args); + return frag; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + String title = getArguments().getString("title"); + String message = getArguments().getString("message"); + String confirm = getArguments().getString("confirm"); + + return new AlertDialog.Builder(getActivity()) + .setIcon(android.R.drawable.ic_dialog_alert) + .setTitle(title) + .setMessage(message) + .setPositiveButton(confirm, mConfirmClickListener) + .setNegativeButton("Cancel", new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dismiss(); + } + }) + .create(); + } + + public void setConfirmationClicklistener(OnClickListener cl) { + mConfirmClickListener = cl; + } + + public interface ConfirmationDialogListener { + void onDialogRead(); + void onDialogChecked(boolean isChecked); + } +} diff --git a/android/BOINC/src/edu/berkeley/boinc/TasksActivity.java b/android/BOINC/src/edu/berkeley/boinc/TasksActivity.java index 00a33866c3..4c73d330c4 100644 --- a/android/BOINC/src/edu/berkeley/boinc/TasksActivity.java +++ b/android/BOINC/src/edu/berkeley/boinc/TasksActivity.java @@ -21,30 +21,54 @@ package edu.berkeley.boinc; import java.util.ArrayList; import edu.berkeley.boinc.adapter.TasksListAdapter; +import edu.berkeley.boinc.client.ClientStatus; import edu.berkeley.boinc.client.Monitor; import edu.berkeley.boinc.rpc.Result; -import android.app.Activity; +import edu.berkeley.boinc.utils.BOINCDefs; +import android.app.Service; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.content.ServiceConnection; import android.os.Bundle; +import android.os.IBinder; +import android.support.v4.app.FragmentActivity; import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; import android.widget.ListView; -import android.widget.TextView; -public class TasksActivity extends Activity { +public class TasksActivity extends FragmentActivity { private final String TAG = "BOINC TasksActivity"; + + private Monitor monitor; + private Boolean mIsBound; + + private ClientStatus status; //client status, new information gets parsed by monitor, changes notified by "clientstatus" broadcast. read Result from here, to get information about tasks. private ListView lv; private TasksListAdapter listAdapter; - private ArrayList data = new ArrayList(); //Adapter for list data - - // Controls whether initialization of view elements of "tasks_layout" - // is required. This is the case, every time the layout switched. - private Boolean initialSetupRequired = true; + private ArrayList data = new ArrayList(); //Adapter for list data + private Boolean setup = false; + + // This is called when the connection with the service has been established, + // getService returns the Monitor object that is needed to call functions. + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + monitor = ((Monitor.LocalBinder)service).getService(); + mIsBound = true; + } + + public void onServiceDisconnected(ComponentName className) { + monitor = null; + mIsBound = false; + } + }; private BroadcastReceiver mClientStatusChangeRec = new BroadcastReceiver() { @@ -52,15 +76,26 @@ public class TasksActivity extends Activity { @Override public void onReceive(Context context,Intent intent) { Log.d(TAG,"onReceive"); - populateLayout(); + loadData(); } }; private IntentFilter ifcsc = new IntentFilter("edu.berkeley.boinc.clientstatuschange"); public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - Log.d(TAG,"onCreate"); + + setContentView(R.layout.tasks_layout); + // Establish a connection with the service, onServiceConnected gets called when + // (calling within Tab needs getApplicationContext() for bindService to work!) + getApplicationContext().bindService(new Intent(this, Monitor.class), mConnection, Service.START_STICKY_COMPATIBILITY); + + //get singleton client status from monitor + status = Monitor.getClientStatus(); + + //load data model + loadData(); + + Log.d(TAG, "onCreate"); } public void onResume() { @@ -68,7 +103,7 @@ public class TasksActivity extends Activity { //register noisy clientStatusChangeReceiver here, so only active when Activity is visible Log.d(TAG+"-onResume","register receiver"); registerReceiver(mClientStatusChangeRec,ifcsc); - populateLayout(); + loadData(); } public void onPause() { @@ -78,44 +113,167 @@ public class TasksActivity extends Activity { super.onPause(); } + @Override + protected void onDestroy() { + Log.d(TAG, "onDestroy()"); + + if (mIsBound) { + getApplicationContext().unbindService(mConnection); + mIsBound = false; + } + + super.onDestroy(); + } - private void populateLayout() { - try { - //setup list and adapter - ArrayList tmpA = Monitor.getClientStatus().getTasks(); - - if(tmpA == null) { - setLayoutLoading(); - return; - } - + private void loadData() { + //setup list and adapter + ArrayList tmpA = status.getTasks(); + if(tmpA!=null) { //can be null before first monitor status cycle (e.g. when not logged in or during startup) + //deep copy, so ArrayList adapter actually recognizes the difference - data.clear(); - for (Result tmp: tmpA) { - data.add(tmp); - } - - if(initialSetupRequired) {// first time we got proper results, setup adapter - initialSetupRequired = false; - setContentView(R.layout.tasks_layout); + updateData(tmpA); + + if(!setup) { //first time we got proper results, setup adapter lv = (ListView) findViewById(R.id.tasksList); - listAdapter = new TasksListAdapter(TasksActivity.this,R.id.tasksList,data); - lv.setAdapter(listAdapter); - } + listAdapter = new TasksListAdapter(TasksActivity.this,R.id.tasksList,data); + lv.setAdapter(listAdapter); + + setup = true; + } Log.d(TAG,"loadData: array contains " + data.size() + " results."); listAdapter.notifyDataSetChanged(); //force list adapter to refresh - - } catch (Exception e) { - // data retrieval failed, set layout to loading... - setLayoutLoading(); + + } else { + Log.d(TAG, "loadData array is null"); } } - - private void setLayoutLoading() { - setContentView(R.layout.generic_layout_loading); - TextView loadingHeader = (TextView)findViewById(R.id.loading_header); - loadingHeader.setText(R.string.tasks_loading); - initialSetupRequired = true; + + private void updateData(ArrayList newData) { + // Create a new task data list based on new data + ArrayList tdl = new ArrayList(); + for (Result r : newData) { + tdl.add(new TaskData(r)); + } + // search for the same id in the old tasks + // maybe tasks were rearranged or updated + for (int i = 0; i < tdl.size(); i++) { + int j; + for (j = 0; j < data.size(); j++) { + if (tdl.get(i).id.equals(data.get(j).id)) { + // this task is old. + // retrieve expansion state + tdl.get(i).expanded = data.get(j).expanded; + break; + } + } + if (j == data.size()) { + // this is a new task, so add it + data.add(tdl.get(i)); + } + } + // save new task data list + data.clear(); + + for (TaskData td: tdl) { + data.add(td); + } + } + + public class TaskData { + public static final int TASK_STATE_UNKNOWN = 0; + public static final int TASK_STATE_PAUSED = 1; + public static final int TASK_STATE_RUNNING = 2; + + public static final int TASK_STATE_ACTIVE = 10; + public static final int TASK_STATE_ABORTED = 11; + + public Result result = null; + public boolean expanded = false; + public String id = ""; + public int nextRunState = TASK_STATE_UNKNOWN; + public int currentRunState = TASK_STATE_UNKNOWN; + public int nextAbortState = TASK_STATE_UNKNOWN; + public int currentAbortState = TASK_STATE_UNKNOWN; + + public TaskData(Result data) { + this.result = data; + this.expanded = false; + this.id = data.name; + this.currentRunState = determineRunningState(); + this.nextRunState = this.currentRunState; + this.currentAbortState = determineAbortState(); + this.nextAbortState = this.currentAbortState; + } + + public final OnClickListener taskClickListener = new OnClickListener() { + @Override + public void onClick(View v) { + expanded = !expanded; + listAdapter.notifyDataSetChanged(); //force list adapter to refresh + } + }; + + public final OnClickListener abortClickListener = new OnClickListener() { + @Override + public void onClick(View v) { + ConfirmationDialog cd = ConfirmationDialog.newInstance( + getString(R.string.confirm_abort_task_title) + "?", + getString(R.string.confirm_abort_task_message) + " " + result.name, + getString(R.string.confirm_abort_task_confirm)); + cd.setConfirmationClicklistener(new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + nextAbortState = TASK_STATE_ABORTED; + monitor.abortResultAsync(result.project_url, result.name); + } + }); + cd.show(getSupportFragmentManager(), ""); + listAdapter.notifyDataSetChanged(); //force list adapter to refresh + } + }; + + public final OnClickListener suspendClickListener = new OnClickListener() { + @Override + public void onClick(View v) { + nextRunState = TASK_STATE_PAUSED; + monitor.suspendResultAsync(result.project_url, result.name); + listAdapter.notifyDataSetChanged(); //force list adapter to refresh + } + }; + + public final OnClickListener resumeClickListener = new OnClickListener() { + @Override + public void onClick(View v) { + nextRunState = TASK_STATE_RUNNING; + monitor.resumeResultAsync(result.project_url, result.name); + listAdapter.notifyDataSetChanged(); //force list adapter to refresh + } + }; + + public int determineRunningState() { + if(result.active_task) { + if(result.active_task_state == BOINCDefs.PROCESS_EXECUTING) { + //running + return TASK_STATE_RUNNING; + } else { + //suspended - ready to run + return TASK_STATE_PAUSED; + } + } else { + //paused or stopped + return TASK_STATE_PAUSED; + } + } + + public int determineAbortState() { + if(!result.active_task) { + switch (result.state) { + case 6: + return TASK_STATE_ABORTED; + } + } + return TASK_STATE_ACTIVE; + } } } diff --git a/android/BOINC/src/edu/berkeley/boinc/adapter/TasksListAdapter.java b/android/BOINC/src/edu/berkeley/boinc/adapter/TasksListAdapter.java index 1f5e4f9ba3..3213f1fa90 100644 --- a/android/BOINC/src/edu/berkeley/boinc/adapter/TasksListAdapter.java +++ b/android/BOINC/src/edu/berkeley/boinc/adapter/TasksListAdapter.java @@ -18,56 +18,63 @@ ******************************************************************************/ package edu.berkeley.boinc.adapter; +import java.sql.Date; import java.util.ArrayList; import edu.berkeley.boinc.R; -import edu.berkeley.boinc.rpc.Result; +import edu.berkeley.boinc.TasksActivity.TaskData; import edu.berkeley.boinc.utils.BOINCDefs; import android.app.Activity; import android.content.Context; +import android.text.format.DateFormat; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.ProgressBar; +import android.widget.RelativeLayout; import android.widget.TextView; -public class TasksListAdapter extends ArrayAdapter{ +public class TasksListAdapter extends ArrayAdapter{ //private final String TAG = "TasksListAdapter"; - private ArrayList entries; - private Activity activity; + private ArrayList entries; + private Activity activity; - public TasksListAdapter(Activity a, int textViewResourceId, ArrayList entries) { - super(a, textViewResourceId, entries); - this.entries = entries; - this.activity = a; - } + public TasksListAdapter(Activity a, int textViewResourceId, ArrayList entries) { + super(a, textViewResourceId, entries); + this.entries = entries; + this.activity = a; + } - @Override - public View getView(int position, View convertView, ViewGroup parent) { + @Override + public View getView(int position, View convertView, ViewGroup parent) { - View v = convertView; - LayoutInflater vi = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - v = vi.inflate(R.layout.tasks_layout_listitem, null); + View v = convertView; + LayoutInflater vi = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + v = vi.inflate(R.layout.tasks_layout_listitem, null); + v.setOnClickListener(entries.get(position).taskClickListener); + ProgressBar pb = (ProgressBar) v.findViewById(R.id.progressBar); TextView header = (TextView) v.findViewById(R.id.taskName); TextView status = (TextView) v.findViewById(R.id.taskStatus); TextView time = (TextView) v.findViewById(R.id.taskTime); TextView progress = (TextView) v.findViewById(R.id.taskProgress); - Result listItem = entries.get(position); - + TaskData listItem = entries.get(position); + pb.setIndeterminate(false); - pb.setProgressDrawable(this.activity.getResources().getDrawable((determineProgressBarLayout(listItem)))); + pb.setProgressDrawable(this.activity.getResources().getDrawable((determineProgressBarLayout(listItem)))); //v.setTag(listItem.name); - String headerT = "Name: " + listItem.name; + String headerT = "Name: " + listItem.result.name; header.setText(headerT); - Float fraction = listItem.fraction_done; - if(!listItem.active_task && listItem.ready_to_report) { //fraction not available, set it to 100 + Float fraction = listItem.result.fraction_done; + if(!listItem.result.active_task && listItem.result.ready_to_report) { //fraction not available, set it to 100 fraction = Float.valueOf((float) 1.0); } pb.setProgress(Math.round(fraction * pb.getMax())); @@ -77,83 +84,142 @@ public class TasksListAdapter extends ArrayAdapter{ String statusT = determineStatusText(listItem); status.setText(statusT); - int elapsedTime = (int)listItem.elapsed_time; + int elapsedTime = (int)listItem.result.elapsed_time; time.setText(String.format("%02d:%02d:%02d", elapsedTime/3600, (elapsedTime/60)%60, elapsedTime%60)); - - return v; - } + + RelativeLayout ll = (RelativeLayout) v.findViewById(R.id.expansion); + if (listItem.expanded) { + ll.setVisibility(View.VISIBLE); + // update resume/suspend state (button state) + listItem.currentRunState = listItem.determineRunningState(); + // update abort state (button state) + listItem.currentAbortState = listItem.determineAbortState(); + // set deadline + String deadline = (String) DateFormat.format("E d MMM yyyy hh:mm:ss aa", new Date(listItem.result.report_deadline*1000)); + ((TextView) v.findViewById(R.id.deadline)).setText("Deadline: " + deadline); + // set project name + ((TextView) v.findViewById(R.id.projectName)).setText("Project name: " + listItem.result.project_url); + // TODO: set application friendly name + + ImageView suspendResume = (ImageView) v.findViewById(R.id.suspendResumeTask); + if (listItem.currentRunState == listItem.nextRunState) { + // current button state is same as expected + suspendResume.setEnabled(true); + suspendResume.setClickable(true); + if (listItem.currentRunState == TaskData.TASK_STATE_RUNNING) { + suspendResume.setOnClickListener(listItem.suspendClickListener); + suspendResume.setImageResource(R.drawable.pausew24); + } else { + suspendResume.setOnClickListener(listItem.resumeClickListener); + suspendResume.setImageResource(R.drawable.playw24); + } + } else { + // waiting for transient state to be as expected + // button needs to be disabled (no action should be taken while waiting) + suspendResume.setEnabled(false); + suspendResume.setClickable(false); + suspendResume.setOnClickListener(null); + if (listItem.currentRunState == TaskData.TASK_STATE_RUNNING) { + suspendResume.setImageResource(R.drawable.pausew24); + } else { + suspendResume.setImageResource(R.drawable.playw24); + } + } + if (listItem.currentAbortState == listItem.nextAbortState) { + // current button state is same as expected + if (listItem.currentAbortState == TaskData.TASK_STATE_ABORTED) { + suspendResume.setEnabled(false); + suspendResume.setClickable(false); + } else { + suspendResume.setEnabled(true); + suspendResume.setClickable(true); + ((ImageView) v.findViewById(R.id.abortTask)).setOnClickListener(listItem.abortClickListener); + } + } else { + // waiting for transient state to be as expected + // button needs to be disabled (no action should be taken while waiting) + suspendResume.setEnabled(false); + suspendResume.setClickable(false); + suspendResume.setOnClickListener(null); + } + } else { + ll.setVisibility(View.GONE); + } + + return v; + } + + private String determineStatusText(TaskData tmp) { + String text = ""; + if(tmp.result.active_task) { + switch (tmp.result.active_task_state) { + case 0: + text = activity.getString(R.string.tasks_active_uninitialized); + break; + case 1: + text = activity.getString(R.string.tasks_active_executing); + break; + case 5: + text = activity.getString(R.string.tasks_active_abort_pending); + break; + case 8: + text = activity.getString(R.string.tasks_active_quit_pending); + break; + case 9: + text = activity.getString(R.string.tasks_active_suspended); + break; + } + } else { + switch (tmp.result.state) { + case 0: + text = activity.getString(R.string.tasks_result_new); + break; + case 1: + text = activity.getString(R.string.tasks_result_files_downloading); + break; + case 2: + text = activity.getString(R.string.tasks_result_files_downloaded); + break; + case 3: + text = activity.getString(R.string.tasks_result_compute_error); + break; + case 4: + text = activity.getString(R.string.tasks_result_files_uploading); + break; + case 5: + text = activity.getString(R.string.tasks_result_files_uploaded); + break; + case 6: + text = activity.getString(R.string.tasks_result_aborted); + break; + case 7: + text = activity.getString(R.string.tasks_result_upload_failed); + break; + } + } + return text; + } - private String determineStatusText(Result tmp) { - String text = ""; - if(tmp.active_task) { - switch (tmp.active_task_state) { - case 0: - text = activity.getString(R.string.tasks_active_uninitialized); - break; - case 1: - text = activity.getString(R.string.tasks_active_executing); - break; - case 5: - text = activity.getString(R.string.tasks_active_abort_pending); - break; - case 8: - text = activity.getString(R.string.tasks_active_quit_pending); - break; - case 9: - text = activity.getString(R.string.tasks_active_suspended); - break; - } - } else { - switch (tmp.state) { - case 0: - text = activity.getString(R.string.tasks_result_new); - break; - case 1: - text = activity.getString(R.string.tasks_result_files_downloading); - break; - case 2: - text = activity.getString(R.string.tasks_result_files_downloaded); - break; - case 3: - text = activity.getString(R.string.tasks_result_compute_error); - break; - case 4: - text = activity.getString(R.string.tasks_result_files_uploading); - break; - case 5: - text = activity.getString(R.string.tasks_result_files_uploaded); - break; - case 6: - text = activity.getString(R.string.tasks_result_aborted); - break; - case 7: - text = activity.getString(R.string.tasks_result_upload_failed); - break; - } - } - return text; - } - - private Integer determineProgressBarLayout(Result tmp) { - if(tmp.active_task) { - if(tmp.active_task_state == BOINCDefs.PROCESS_EXECUTING) { - //running - return R.drawable.progressbar_active; - } else { - //suspended - ready to run - return R.drawable.progressbar_paused; - } - } else { - if((tmp.state == BOINCDefs.RESULT_ABORTED) || (tmp.state == BOINCDefs.RESULT_UPLOAD_FAILED) || (tmp.state == BOINCDefs.RESULT_COMPUTE_ERROR)) { - //error - return R.drawable.progressbar_error; - } else if (tmp.ready_to_report) { - //finished - return R.drawable.progressbar_active; - } else { - //paused or stopped - return R.drawable.progressbar_paused; - } - } - } + private Integer determineProgressBarLayout(TaskData tmp) { + if(tmp.result.active_task) { + if(tmp.result.active_task_state == BOINCDefs.PROCESS_EXECUTING) { + //running + return R.drawable.progressbar_active; + } else { + //suspended - ready to run + return R.drawable.progressbar_paused; + } + } else { + if((tmp.result.state == BOINCDefs.RESULT_ABORTED) || (tmp.result.state == BOINCDefs.RESULT_UPLOAD_FAILED) || (tmp.result.state == BOINCDefs.RESULT_COMPUTE_ERROR)) { + //error + return R.drawable.progressbar_error; + } else if (tmp.result.ready_to_report) { + //finished + return R.drawable.progressbar_active; + } else { + //paused or stopped + return R.drawable.progressbar_paused; + } + } + } } diff --git a/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java b/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java index 6726f35f8e..fed3ed954a 100644 --- a/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java +++ b/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java @@ -810,6 +810,42 @@ public class Monitor extends Service { param[1] = name; (new TransferRetryAsync()).execute(param); } + + public Boolean suspendResult(String url, String name){ + return rpc.resultOp(RpcClient.RESULT_SUSPEND, url, name); + } + + public void suspendResultAsync(String url, String name){ + Log.d(TAG, "suspendResultAsync"); + String[] param = new String[2]; + param[0] = url; + param[1] = name; + (new SuspendResultAsync()).execute(param); + } + + public Boolean resumeResult(String url, String name){ + return rpc.resultOp(RpcClient.RESULT_RESUME, url, name); + } + + public void resumeResultAsync(String url, String name){ + Log.d(TAG, "resumeResultAsync"); + String[] param = new String[2]; + param[0] = url; + param[1] = name; + (new ResumeResultAsync()).execute(param); + } + + public Boolean abortResult(String url, String name){ + return rpc.resultOp(RpcClient.RESULT_RESUME, url, name); + } + + public void abortResultAsync(String url, String name){ + Log.d(TAG, "abortResultAsync"); + String[] param = new String[2]; + param[0] = url; + param[1] = name; + (new AbortResultAsync()).execute(param); + } public AccountOut createAccount(String url, String email, String userName, String pwd, String teamName) { AccountIn information = new AccountIn(); @@ -1067,6 +1103,98 @@ public class Monitor extends Service { } } + private final class SuspendResultAsync extends AsyncTask { + + private final String TAG = "SuspendResultAsync"; + + private String url; + private String name; + + @Override + protected Boolean doInBackground(String... params) { + this.url = params[0]; + this.name = params[1]; + publishProgress("doInBackground() - SuspendResultAsync url: " + url + " Name: " + name); + + Boolean retry = rpc.resultOp(RpcClient.RESULT_SUSPEND, url, name); + if(retry) { + publishProgress("successful."); + } + return retry; + } + + @Override + protected void onPostExecute(Boolean success) { + forceRefresh(); + } + + @Override + protected void onProgressUpdate(String... arg0) { + Log.d(TAG, "onProgressUpdate - " + arg0[0]); + } + } + + private final class ResumeResultAsync extends AsyncTask { + + private final String TAG = "ResumeResultAsync"; + + private String url; + private String name; + + @Override + protected Boolean doInBackground(String... params) { + this.url = params[0]; + this.name = params[1]; + publishProgress("doInBackground() - ResumeResultAsync url: " + url + " Name: " + name); + Boolean retry = rpc.resultOp(RpcClient.RESULT_RESUME, url, name); + if(retry) { + publishProgress("successful."); + } + return retry; + } + + @Override + protected void onPostExecute(Boolean success) { + forceRefresh(); + } + + @Override + protected void onProgressUpdate(String... arg0) { + Log.d(TAG, "onProgressUpdate - " + arg0[0]); + } + } + + private final class AbortResultAsync extends AsyncTask { + + private final String TAG = "AbortResultAsync"; + + private String url; + private String name; + + @Override + protected Boolean doInBackground(String... params) { + this.url = params[0]; + this.name = params[1]; + publishProgress("doInBackground() - AbortResultAsync url: " + url + " Name: " + name); + + Boolean retry = rpc.resultOp(RpcClient.RESULT_ABORT, url, name); + if(retry) { + publishProgress("successful."); + } + return retry; + } + + @Override + protected void onPostExecute(Boolean success) { + forceRefresh(); + } + + @Override + protected void onProgressUpdate(String... arg0) { + Log.d(TAG, "onProgressUpdate - " + arg0[0]); + } + } + private final class WriteClientRunModeAsync extends AsyncTask { private final String TAG = "WriteClientRunModeAsync"; diff --git a/android/BOINC/src/edu/berkeley/boinc/receiver/BootReceiver.java b/android/BOINC/src/edu/berkeley/boinc/receiver/BootReceiver.java index 9e0b7dced9..ce62d7fe56 100644 --- a/android/BOINC/src/edu/berkeley/boinc/receiver/BootReceiver.java +++ b/android/BOINC/src/edu/berkeley/boinc/receiver/BootReceiver.java @@ -22,8 +22,6 @@ import edu.berkeley.boinc.AppPreferences; import edu.berkeley.boinc.BOINCActivity; import edu.berkeley.boinc.R; import edu.berkeley.boinc.client.Monitor; -import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; @@ -34,7 +32,6 @@ import android.util.Log; public class BootReceiver extends BroadcastReceiver { private final String TAG = "BOINC BootReceiver"; - private NotificationManager mNM; @Override public void onReceive(Context context, Intent intent) { @@ -48,15 +45,8 @@ public class BootReceiver extends BroadcastReceiver { //startServiceIntent.putExtra("autostart", true); context.startService(startServiceIntent); - mNM = (NotificationManager) context.getSystemService(Service.NOTIFICATION_SERVICE); - Notification notification = new Notification(R.drawable.boinc, context.getString(R.string.autostart_notification_header), System.currentTimeMillis()); PendingIntent contentIntent = PendingIntent.getActivity(context.getApplicationContext(), 0, new Intent(context.getApplicationContext(), BOINCActivity.class), 0); - // Set current view for notification panel - notification.setLatestEventInfo(context.getApplicationContext(), context.getString(R.string.autostart_notification_header), context.getString(R.string.autostart_notification_text), contentIntent); - - // Send notification - mNM.notify(context.getResources().getInteger(R.integer.autostart_notification_id), notification); } else { // do nothing Log.d(TAG,"autostart disabeld"); diff --git a/android/BOINC/src/edu/berkeley/boinc/rpc/RpcClient.java b/android/BOINC/src/edu/berkeley/boinc/rpc/RpcClient.java index b5243ee9ea..9a4887cf2e 100644 --- a/android/BOINC/src/edu/berkeley/boinc/rpc/RpcClient.java +++ b/android/BOINC/src/edu/berkeley/boinc/rpc/RpcClient.java @@ -1103,4 +1103,52 @@ public class RpcClient { return false; } } + + /** + * Triggers operation on task in BOINC core client + * @param operation operation to be triggered + * @param projectUrl master URL of project + * @param fileName name of the file + * @return true for success, false for failure + */ + public boolean resultOp(int operation, String projectUrl, String resultName) { + try { + String opTag; + switch (operation) { + case RESULT_SUSPEND: + opTag = "suspend_result"; + break; + case RESULT_RESUME: + opTag = "resume_result"; + break; + case RESULT_ABORT: + opTag = "abort_result"; + break; + default: + if (Logging.ERROR) Log.e(TAG, "resultOp() - unsupported operation: " + operation); + return false; + } + mRequest.setLength(0); + mRequest.append("<"); + mRequest.append(opTag); + mRequest.append(">\n "); + mRequest.append(projectUrl); + mRequest.append("\n "); + mRequest.append(resultName); + mRequest.append("\n\n"); + sendRequest(mRequest.toString()); + + SimpleReplyParser parser = SimpleReplyParser.parse(receiveReply()); + if (parser == null) + return false; + mLastErrorMessage = parser.getErrorMessage(); + return parser.result(); + } + catch (IOException e) { + if (Logging.WARNING) Log.w(TAG, "error in transferOp()", e); + return false; + } + } }