You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tasks/astrid/src/main/java/com/todoroo/astrid/activity/AstridActivity.java

397 lines
14 KiB
Java

/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.speech.SpeechRecognizer;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.WindowManager.BadTokenException;
import android.widget.EditText;
import android.widget.Toast;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.actfm.CommentsFragment;
import com.todoroo.astrid.actfm.TagCommentsFragment;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.FilterListItem;
import com.todoroo.astrid.api.FilterWithCustomIntent;
import com.todoroo.astrid.core.CoreFilterExposer;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.service.StartupService;
import com.todoroo.astrid.subtasks.SubtasksHelper;
import com.todoroo.astrid.ui.DateChangedAlerts;
import com.todoroo.astrid.ui.QuickAddBar;
import com.todoroo.astrid.voice.RecognizerApi.RecognizerApiListener;
import com.todoroo.astrid.voice.VoiceRecognizer;
import org.tasks.R;
/**
* This wrapper activity contains all the glue-code to handle the callbacks between the different
* fragments that could be visible on the screen in landscape-mode.
* So, it basically contains all the callback-code from the filterlist-fragment, the tasklist-fragments
* and the taskedit-fragment (and possibly others that should be handled).
* Using this AstridWrapperActivity helps to avoid duplicated code because its all gathered here for sub-wrapperactivities
* to use.
*
* @author Arne
*
*/
public class AstridActivity extends ActionBarActivity
implements FilterListFragment.OnFilterItemClickedListener,
TaskListFragment.OnTaskListItemClickedListener,
RecognizerApiListener {
public static final int LAYOUT_SINGLE = 0;
public static final int LAYOUT_DOUBLE = 1;
public static final int RESULT_RESTART_ACTIVITY = 50;
protected int fragmentLayout = LAYOUT_SINGLE;
private final RepeatConfirmationReceiver repeatConfirmationReceiver = new RepeatConfirmationReceiver();
@Autowired private TaskDao taskDao;
public FilterListFragment getFilterListFragment() {
return (FilterListFragment) getSupportFragmentManager()
.findFragmentByTag(FilterListFragment.TAG_FILTERLIST_FRAGMENT);
}
public TaskListFragment getTaskListFragment() {
return (TaskListFragment) getSupportFragmentManager()
.findFragmentByTag(TaskListFragment.TAG_TASKLIST_FRAGMENT);
}
public TaskEditFragment getTaskEditFragment() {
return (TaskEditFragment) getSupportFragmentManager()
.findFragmentByTag(TaskEditFragment.TAG_TASKEDIT_FRAGMENT);
}
public CommentsFragment getTagUpdatesFragment() {
return (CommentsFragment) getSupportFragmentManager()
.findFragmentByTag(CommentsFragment.TAG_UPDATES_FRAGMENT);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
AstridDependencyInjector.inject(this);
super.onCreate(savedInstanceState);
ContextManager.setContext(this);
new StartupService().onStartupApplication(this);
}
@Override
protected void onResume() {
super.onResume();
android.content.IntentFilter repeatFilter = new android.content.IntentFilter(
AstridApiConstants.BROADCAST_EVENT_TASK_REPEATED);
repeatFilter.addAction(AstridApiConstants.BROADCAST_EVENT_TASK_REPEAT_FINISHED);
registerReceiver(repeatConfirmationReceiver, repeatFilter);
}
@Override
protected void onPause() {
super.onPause();
AndroidUtilities.tryUnregisterReceiver(this, repeatConfirmationReceiver);
}
/**
* Handles items being clicked from the filterlist-fragment. Return true if item is handled.
*/
@Override
public boolean onFilterItemClicked(FilterListItem item) {
if (this instanceof TaskListActivity && (item instanceof Filter) ) {
((TaskListActivity) this).setSelectedItem((Filter) item);
}
// If showing both fragments, directly update the tasklist-fragment
Intent intent = getIntent();
if(item instanceof Filter) {
Filter filter = (Filter)item;
Bundle extras = configureIntentAndExtrasWithFilter(intent, filter);
if (fragmentLayout == LAYOUT_DOUBLE && getTaskEditFragment() != null) {
onBackPressed(); // remove the task edit fragment when switching between lists
}
setupTasklistFragmentWithFilter(filter, extras);
// no animation for dualpane-layout
AndroidUtilities.callOverridePendingTransition(this, 0, 0);
return true;
}
return false;
}
protected Bundle configureIntentAndExtrasWithFilter(Intent intent, Filter filter) {
if(filter instanceof FilterWithCustomIntent) {
int lastSelectedList = intent.getIntExtra(FilterListFragment.TOKEN_LAST_SELECTED, 0);
intent = ((FilterWithCustomIntent)filter).getCustomIntent();
intent.putExtra(FilterListFragment.TOKEN_LAST_SELECTED, lastSelectedList);
} else {
intent.putExtra(TaskListFragment.TOKEN_FILTER, filter);
}
setIntent(intent);
Bundle extras = intent.getExtras();
if (extras != null) {
extras = (Bundle) extras.clone();
}
return extras;
}
public void setupActivityFragment(TagData tagData) {
if (fragmentLayout == LAYOUT_SINGLE) {
return;
}
if (fragmentLayout == LAYOUT_DOUBLE) {
findViewById(R.id.taskedit_fragment_container).setVisibility(View.VISIBLE);
}
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
TagCommentsFragment updates = new TagCommentsFragment(tagData);
transaction.replace(R.id.taskedit_fragment_container, updates, CommentsFragment.TAG_UPDATES_FRAGMENT);
transaction.commit();
}
public void setupTasklistFragmentWithFilter(Filter filter, Bundle extras) {
Class<?> customTaskList = null;
if (SubtasksHelper.shouldUseSubtasksFragmentForFilter(filter)) {
customTaskList = SubtasksHelper.subtasksClassForFilter(filter);
}
setupTasklistFragmentWithFilterAndCustomTaskList(filter, extras, customTaskList);
}
public void setupTasklistFragmentWithFilterAndCustomTaskList(Filter filter, Bundle extras, Class<?> customTaskList) {
TaskListFragment newFragment = TaskListFragment.instantiateWithFilterAndExtras(filter, extras, customTaskList);
try {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.tasklist_fragment_container, newFragment,
TaskListFragment.TAG_TASKLIST_FRAGMENT);
transaction.commit();
runOnUiThread(new Runnable() {
@Override
public void run() {
getSupportFragmentManager().executePendingTransactions();
}
});
} catch (Exception e) {
// Don't worry about it
e.printStackTrace();
}
}
@Override
public void onTaskListItemClicked(long taskId) {
editTask(taskId);
}
private void editTask(long taskId) {
Intent intent = new Intent(this, TaskEditActivity.class);
intent.putExtra(TaskEditFragment.TOKEN_ID, taskId);
getIntent().putExtra(TaskEditFragment.TOKEN_ID, taskId); // Needs to be in activity intent so that TEA onResume doesn't create a blank activity
if (getIntent().hasExtra(TaskListFragment.TOKEN_FILTER)) {
intent.putExtra(TaskListFragment.TOKEN_FILTER, getIntent().getParcelableExtra(TaskListFragment.TOKEN_FILTER));
}
if (fragmentLayout != LAYOUT_SINGLE) {
TaskEditFragment editActivity = getTaskEditFragment();
findViewById(R.id.taskedit_fragment_container).setVisibility(View.VISIBLE);
if(editActivity == null) {
editActivity = new TaskEditFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.taskedit_fragment_container, editActivity, TaskEditFragment.TAG_TASKEDIT_FRAGMENT);
transaction.addToBackStack(null);
transaction.commit();
runOnUiThread(new Runnable() {
@Override
public void run() {
// Force the transaction to occur so that we can be guaranteed of the fragment existing if we try to present it
getSupportFragmentManager().executePendingTransactions();
}
});
} else {
editActivity.save(true);
editActivity.repopulateFromScratch(intent);
}
TaskListFragment tlf = getTaskListFragment();
if (tlf != null) {
tlf.loadTaskListContent(true);
}
} else {
startActivityForResult(intent, TaskListFragment.ACTIVITY_EDIT_TASK);
AndroidUtilities.callOverridePendingTransition(this, R.anim.slide_left_in, R.anim.slide_left_out);
}
}
@Override
public void onBackPressed() {
if (isFinishing()) {
return;
}
super.onBackPressed();
}
// --- fragment helpers
protected Fragment setupFragment(String tag, int container, Class<? extends Fragment> cls) {
final FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentByTag(tag);
if(fragment == null) {
try {
fragment = cls.newInstance();
} catch (InstantiationException e) {
return null;
} catch (IllegalAccessException e) {
return null;
}
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (container == 0) {
ft.add(fragment, tag);
}
else {
ft.replace(container, fragment, tag);
}
ft.commit();
runOnUiThread(new Runnable() {
@Override
public void run() {
fm.executePendingTransactions();
}
});
}
return fragment;
}
// Voice recognizer callbacks
@Override
public void onSpeechResult(String result) {
TaskListFragment tlf = getTaskListFragment();
if (tlf != null) {
EditText box = tlf.quickAddBar.getQuickAddBox();
if (box != null) {
box.setText(result);
}
}
}
@Override
public void onSpeechError(int error) {
TaskListFragment tlf = getTaskListFragment();
if (tlf != null) {
QuickAddBar quickAdd = tlf.quickAddBar;
if (quickAdd != null) {
VoiceRecognizer vr = quickAdd.getVoiceRecognizer();
if (vr != null) {
vr.cancel();
}
}
}
int errorStr = 0;
switch(error) {
case SpeechRecognizer.ERROR_NETWORK:
case SpeechRecognizer.ERROR_NETWORK_TIMEOUT:
errorStr = R.string.speech_err_network;
break;
case SpeechRecognizer.ERROR_NO_MATCH:
Toast.makeText(this, R.string.speech_err_no_match, Toast.LENGTH_LONG).show();
break;
default:
errorStr = R.string.speech_err_default;
break;
}
if (errorStr > 0) {
DialogUtilities.okDialog(this, getString(errorStr), null);
}
}
public void switchToActiveTasks() {
onFilterItemClicked(CoreFilterExposer.buildInboxFilter(getResources()));
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_RESTART_ACTIVITY) {
finish();
startActivity(getIntent());
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
/**
* @return LAYOUT_SINGLE or LAYOUT_DOUBLE
*/
public int getFragmentLayout() {
return fragmentLayout;
}
private class RepeatConfirmationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, final Intent intent) {
long taskId = intent.getLongExtra(
AstridApiConstants.EXTRAS_TASK_ID, 0);
if (taskId > 0) {
long oldDueDate = intent.getLongExtra(
AstridApiConstants.EXTRAS_OLD_DUE_DATE, 0);
long newDueDate = intent.getLongExtra(
AstridApiConstants.EXTRAS_NEW_DUE_DATE, 0);
Task task = PluginServices.getTaskService().fetchById(taskId,
DateChangedAlerts.REPEAT_RESCHEDULED_PROPERTIES);
try {
boolean lastTime = AstridApiConstants.BROADCAST_EVENT_TASK_REPEAT_FINISHED.equals(intent.getAction());
DateChangedAlerts.showRepeatTaskRescheduledDialog(
AstridActivity.this, task, oldDueDate, newDueDate, lastTime);
} catch (BadTokenException e) { // Activity not running when tried to show dialog--rebroadcast
new Thread() {
@Override
public void run() {
sendBroadcast(intent);
}
}.start();
}
}
}
}
}