/*******************************************************************************
* Copyright 2011 The Regents of the University of California
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.ohmage.triggers.notif;
import android.app.Dialog;
import android.app.ListActivity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.TextView;
import org.ohmage.R;
import org.ohmage.logprobe.Analytics;
import org.ohmage.logprobe.Log;
import org.ohmage.logprobe.LogProbe.Status;
import org.ohmage.triggers.config.NotifConfig;
import org.ohmage.triggers.utils.TrigTextInput;
import java.util.ArrayList;
import java.util.Iterator;
/*
* Activity to edit the notification description visually.
* Currently, this activity is called by the trigger list
* activity to edit the global notification description.
* It can also be called (without any change) to edit the
* notification description associated with individual trigger
* if required in the future.
*/
public class NotifEditActivity extends ListActivity
implements OnClickListener {
private static final String TAG = "NotifEditActivity";
public static final String KEY_NOTIF_CONFIG = "notif_config";
private static final String KEY_SAVE_LIST_DATA = "list_data";
private static final int LIST_POS_DURATION = 0;
private static final int LIST_POS_SUPPRESSION = 1;
private static final int LIST_POS_REPEAT_HEADER = 2;
private static final int LIST_POS_REPEAT_START = 3;
//Minimum value allowed for notification duration
private static final int DURATION_MIN = 1;
//Minimum value allowed for suppression window
private static final int SUPPRESSION_MIN = 0; //Disables suppression
private ArrayList<Integer> mListData = null;
private NotifDesc mNotifDesc;
private ArrayAdapter<Integer> mListAdapter;
private Dialog mDialog = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Get the notification description from the intent
Intent intent = getIntent();
String desc = intent.getStringExtra(KEY_NOTIF_CONFIG);
if(desc == null) {
Log.e(TAG, "NotifEditActivity: No notification " +
"description passed. Exiting...");
finish();
return;
}
//Parse and load the description
mNotifDesc = new NotifDesc();
if(!mNotifDesc.loadString(desc)) {
Log.e(TAG, "NotifEditActivity: Failed to parse the " +
"notification description passed. Exiting...");
finish();
return;
}
setContentView(R.layout.trigger_editor);
Button b = (Button) findViewById(R.id.trig_edit_done);
b.setOnClickListener(this);
b = (Button) findViewById(R.id.trig_edit_cancel);
b.setOnClickListener(this);
//Update the GUI with the data
initializeListData();
setupListAdaptor();
validateDataAndUpdateView();
}
@Override
protected void onResume() {
super.onResume();
Analytics.activity(this, Status.ON);
}
@Override
protected void onPause() {
super.onPause();
Analytics.activity(this, Status.OFF);
}
@Override
protected void onSaveInstanceState(Bundle out) {
super.onSaveInstanceState(out);
out.putIntegerArrayList(KEY_SAVE_LIST_DATA, mListData);
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
ArrayList<Integer> savedArray = state.getIntegerArrayList(KEY_SAVE_LIST_DATA);
if(mListData != null && savedArray != null) {
mListData.clear();
mListData.addAll(savedArray);
}
mListAdapter.notifyDataSetChanged();
}
private void initializeListData() {
mListData = new ArrayList<Integer>();
//Add duration and suppression at the top
mListData.add(LIST_POS_DURATION, mNotifDesc.getDuration());
mListData.add(LIST_POS_SUPPRESSION, mNotifDesc.getSuppression());
//Add something at the repeat header pos to make the array index
//consistent
mListData.add(LIST_POS_REPEAT_HEADER, 0);
//Now add all the repeat items at the end
mListData.addAll(mNotifDesc.getSortedRepeats());
}
private void setupListAdaptor() {
mListAdapter = new ArrayAdapter<Integer>(this, R.id.repeat_list_text,
mListData) {
@Override
public boolean isEnabled(int pos) {
return (pos == LIST_POS_REPEAT_HEADER) ? false : true;
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
private View getTwoLineView(LayoutInflater inflater, int pos) {
View view = inflater.inflate(R.layout.trigger_two_line_list_item,
null);
TextView tvTitle = (TextView) view.findViewById(R.id.text1);
if(pos == LIST_POS_DURATION) {
tvTitle.setText("Notification duration");
}
else {
tvTitle.setText("Suppression window");
}
TextView tvSummary = (TextView) view.findViewById(R.id.text2);
tvSummary.setText(mListData.get(pos) + " minutes");
return view;
}
private View getRepeatHeaderView(LayoutInflater inflater, int pos) {
View view = inflater.inflate(R.layout.trigger_add_new, null);
TextView tv = (TextView) view.findViewById(R.id.add_new_label);
tv.setText(R.string.trigger_notification_reminder_title);
ImageButton bAdd = (ImageButton) view.findViewById(R.id.button_add_new);
bAdd.setOnClickListener(NotifEditActivity.this);
boolean enable = true;
if((mListData.get(LIST_POS_DURATION) - 1) <
mNotifDesc.getMinAllowedRepeat()) {
enable = false;
}
bAdd.setEnabled(enable);
return view;
}
private View getRepeatItemView(LayoutInflater inflater, int pos) {
View view = inflater.inflate(R.layout.trigger_notif_edit_repeat_row,
null);
ImageButton ib = (ImageButton) view.findViewById(
R.id.button_remove_repeat);
ib.setFocusable(false);
ib.setTag(new Integer(pos));
ib.setOnClickListener(NotifEditActivity.this);
TextView tv = (TextView) view.findViewById(
R.id.repeat_list_text);
tv.setText(String.valueOf(mListData.get(pos)) + " minutes");
return view;
}
@Override
public View getView(int pos, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
View view;
if(pos == LIST_POS_DURATION || pos == LIST_POS_SUPPRESSION) {
view = getTwoLineView(inflater, pos);
}
else if(pos == LIST_POS_REPEAT_HEADER) {
view = getRepeatHeaderView(inflater, pos);
}
else {
view = getRepeatItemView(inflater, pos);
}
return view;
}
};
getListView().setAdapter(mListAdapter);
}
private void validateDataAndUpdateView() {
Iterator<Integer> i = mListData.iterator();
//Skip the initial items until repeat
int pos = 0;
while(i.hasNext() && pos < LIST_POS_REPEAT_START) {
pos++;
i.next();
}
while(i.hasNext()) {
//Remove all items which is greater than the allowed value
//which is max(duration) - 1
if(i.next() > (mListData.get(LIST_POS_DURATION) - 1)) {
i.remove();
}
}
mListAdapter.notifyDataSetChanged();
}
@Override
protected Dialog onCreateDialog(int id) {
if(id >= mListData.size()) {
return null;
}
String title = "";
String text = "";
int max = 0;
int min = 0;
switch(id) {
case LIST_POS_DURATION: //Edit notification duration
title = "Notification duration \n(1 - " +
NotifConfig.maxDuration + ")";
text = String.valueOf(mListData.get(LIST_POS_DURATION));
max = NotifConfig.maxDuration;
min = DURATION_MIN;
break;
case LIST_POS_SUPPRESSION: //Edit suppression window
title = "Suppression window \n(0 - " +
NotifConfig.maxSuppression + ")";
text = String.valueOf(mListData.get(LIST_POS_SUPPRESSION));
max = NotifConfig.maxSuppression;
min = SUPPRESSION_MIN;
break;
case LIST_POS_REPEAT_HEADER: //Repeat header, ignore. Nothing
//to edit here.
return null;
default: //Any other id would correspond to a repeat value
text = String.valueOf(mListData.get(id));
min = mNotifDesc.getMinAllowedRepeat();
max = mListData.get(LIST_POS_DURATION) - 1;
title = "Set reminder \n(" + min + " - " + max + ")";
if(min == max) {
//Nothing to edit. Do nothing
return null;
}
}
//Launch the text edit dialog
TrigTextInput ti = new TrigTextInput(this);
ti.setTitle(title);
ti.setText(text);
ti.setNumberMode(true);
ti.setNumberModeRange(min, max);
ti.setTag(new Integer(id));
ti.setPositiveButtonText("Done");
ti.setNegativeButtonText("Cancel");
ti.setOnClickListener(new TrigTextInput.onClickListener() {
@Override
public void onClick(TrigTextInput textInput, int which) {
if(which == TrigTextInput.BUTTON_POSITIVE) {
int listPos = (Integer) textInput.getTag();
if(listPos < mListData.size()) {
mListData.set(listPos,
Integer.valueOf(textInput.getText()));
//Validate the data
validateDataAndUpdateView();
}
}
}
});
mDialog = ti.createDialog();
return mDialog;
}
@Override
protected void onListItemClick(ListView l, View v, int pos, long id) {
super.onListItemClick(l, v, pos, id);
if(pos != LIST_POS_REPEAT_HEADER) {
//Remove the dialog first to force onCreateDialog().
//We need to prepare the dialog every time based
//on the list index. This is possible without this
//approach in future API levels (by passing a bundle)
//but not in 1.6
removeDialog(pos);
showDialog(pos);
}
}
@Override
public void onClick(View v) {
//Ignore all clicks on the list once a dialog has been
//created. This is needed to guard against quikly pressing
//the buttons multiple times.
if(mDialog != null && mDialog.isShowing()) {
return;
}
switch(v.getId()) {
case R.id.button_remove_repeat: //The 'minus' button
int iList = (Integer) v.getTag();
mListData.remove(iList);
mListAdapter.notifyDataSetChanged();
break;
case R.id.button_add_new: //The 'plus' button
//Find a new value for the repeat and add a
//repeat item based on it. This is done by simply
//adding the default repeat value to the maximum
//value in the list and then bounding it using the
//maximum repeat value (which is max(duration) - 1)
int maxRepeat = 0;
for(int i = LIST_POS_REPEAT_START; i < mListData.size(); i++) {
int repeat = mListData.get(i);
if(repeat > maxRepeat) {
maxRepeat = repeat;
}
}
int duration = mListData.get(LIST_POS_DURATION);
maxRepeat += NotifConfig.defaultRepeat;
if(maxRepeat >= duration) {
maxRepeat = duration - 1;
}
//Add the new repeat item to the list
mListData.add(LIST_POS_REPEAT_START, new Integer(maxRepeat));
mListAdapter.notifyDataSetChanged();
break;
case R.id.trig_edit_done: //Done with editing the notification settings
//TODO validate
//Copy the values from the UI to the description class,
//convert to string and then send back the result
mNotifDesc.setDuration(mListData.get(LIST_POS_DURATION));
mNotifDesc.setSuppression(mListData.get(LIST_POS_SUPPRESSION));
if(LIST_POS_REPEAT_START < mListData.size()) {
mNotifDesc.setRepeats(
mListData.subList(LIST_POS_REPEAT_START, mListData.size()));
}
else {
mNotifDesc.setRepeats(new ArrayList<Integer>());
}
Intent intent = new Intent();
intent.putExtra(KEY_NOTIF_CONFIG, mNotifDesc.toString());
setResult(0, intent);
finish();
break;
case R.id.trig_edit_cancel: //Canceled editing
finish();
break;
}
}
}