/*
* Copyright (C) 2012 Pixmob (http://github.com/pixmob)
*
* 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.pixmob.httpclient.demo;
import static org.pixmob.httpclient.demo.Constants.TAG;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.pixmob.httpclient.UserInteractionRequiredException;
import org.pixmob.httpclient.demo.TaskListFragment.TaskContext;
import org.pixmob.httpclient.demo.tasks.ContentTypeTask;
import org.pixmob.httpclient.demo.tasks.DownloadFileTask;
import org.pixmob.httpclient.demo.tasks.GoogleAppEngineAuthTask;
import org.pixmob.httpclient.demo.tasks.HttpsTask;
import org.pixmob.httpclient.demo.tasks.PostFormTask;
import org.pixmob.httpclient.demo.tasks.RedirectTask;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.actionbarsherlock.app.SherlockListFragment;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
/**
* Fragment for displaying task list.
* @author Pixmob
*/
public class TaskListFragment extends SherlockListFragment implements LoaderCallbacks<TaskContext> {
private static final int USER_INTERACTION_REQUEST = 1337;
private TaskContext[] taskContexts;
private TaskContextAdapter taskContextAdapter;
private MenuItem startMenuItem;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
final Context context = getActivity().getApplicationContext();
taskContexts = new TaskContext[] { new TaskContext(new DownloadFileTask(context)),
new TaskContext(new RedirectTask(context)), new TaskContext(new PostFormTask(context)),
new TaskContext(new ContentTypeTask(context)), new TaskContext(new HttpsTask(context)),
new TaskContext(new GoogleAppEngineAuthTask(context)) };
taskContextAdapter = new TaskContextAdapter(getActivity(), taskContexts);
setListAdapter(taskContextAdapter);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
@SuppressWarnings("unchecked")
final List<TaskState> states = (List<TaskState>) savedInstanceState.getSerializable("taskStates");
for (int i = 0; i < states.size(); ++i) {
taskContexts[i].state = states.get(i);
if (TaskState.RUNNING.equals(states.get(i))) {
getLoaderManager().restartLoader(i, null, this);
}
}
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
final ArrayList<TaskState> states = new ArrayList<TaskListFragment.TaskState>(taskContexts.length);
for (int i = 0; i < taskContexts.length; ++i) {
states.add(taskContexts[i].state);
}
outState.putSerializable("taskStates", states);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (USER_INTERACTION_REQUEST == requestCode) {
final boolean authPermOK = resultCode == Activity.RESULT_OK;
Log.i(TAG, "Did user grant permission for authentication? " + (authPermOK ? "YES" : "NO"));
if (authPermOK) {
Toast.makeText(getActivity(), getString(R.string.access_granted), Toast.LENGTH_SHORT).show();
// Restart authentication.
for (int i = 0; i < taskContexts.length; ++i) {
if (taskContexts[i].task instanceof GoogleAppEngineAuthTask) {
getLoaderManager().restartLoader(i, null, this);
break;
}
}
}
}
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
setSelection(position);
final TaskContext taskContext = taskContextAdapter.getItem(position);
final String url = taskContext.task.getSourceCodeUrl();
if (url != null) {
final Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(i);
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
startMenuItem = menu.add(Menu.NONE, R.string.menu_start_demo, Menu.NONE, R.string.menu_start_demo);
startMenuItem.setIcon(R.drawable.ic_menu_play_clip).setShowAsAction(
MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
menu.add(Menu.NONE, R.string.menu_help, Menu.NONE, R.string.menu_help)
.setIcon(R.drawable.ic_menu_help)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.string.menu_start_demo:
onMenuStart();
break;
case R.string.menu_help:
onMenuHelp();
break;
default:
return false;
}
return true;
}
private void onMenuStart() {
startMenuItem.setTitle(R.string.menu_stop_demo);
startMenuItem.setIcon(R.drawable.ic_menu_stop);
for (final TaskContext taskContext : taskContexts) {
taskContext.reset();
}
getLoaderManager().restartLoader(0, null, this);
}
private void onMenuHelp() {
new HelpDialogFragment().show(getFragmentManager(), "help");
}
@Override
public Loader<TaskContext> onCreateLoader(int id, Bundle args) {
final TaskContext taskContext = taskContexts[id];
taskContext.state = TaskState.RUNNING;
taskContextAdapter.notifyDataSetChanged();
return new TaskExecutor(getActivity(), taskContext);
}
@Override
public void onLoaderReset(Loader<TaskContext> loader) {
}
@Override
public void onLoadFinished(Loader<TaskContext> loader, TaskContext result) {
taskContextAdapter.notifyDataSetChanged();
final int nextId = loader.getId() + 1;
if (nextId < taskContexts.length) {
getLoaderManager().restartLoader(nextId, null, this);
} else {
startMenuItem.setTitle(R.string.menu_start_demo);
startMenuItem.setIcon(R.drawable.ic_menu_play_clip);
for (final TaskContext taskContext : taskContexts) {
if (taskContext.userIntent != null) {
startActivityForResult(taskContext.userIntent, USER_INTERACTION_REQUEST);
break;
}
}
}
}
/**
* Execute {@link Task} instance in a background thread.
* @author Pixmob
*/
private static class TaskExecutor extends AsyncTaskLoader<TaskContext> {
private final TaskContext taskContext;
public TaskExecutor(final FragmentActivity context, final TaskContext taskContext) {
super(context);
this.taskContext = taskContext;
}
@Override
protected void onStartLoading() {
super.onStartLoading();
if (TaskState.RUNNING.equals(taskContext.state)) {
forceLoad();
}
}
@Override
public TaskContext loadInBackground() {
final String taskName = taskContext.task.getName();
Log.i(TAG, "Executing task: " + taskName);
try {
taskContext.task.run();
taskContext.state = TaskState.FINISHED;
Log.i(TAG, "Task successfully executed: " + taskName);
} catch (TaskExecutionFailedException e) {
taskContext.state = TaskState.FAILED;
final Throwable cause = e.getCause();
if (cause != null && cause instanceof UserInteractionRequiredException) {
final UserInteractionRequiredException e2 = (UserInteractionRequiredException) cause;
taskContext.userIntent = e2.getUserIntent();
}
Log.e(TAG, "Task execution failed: " + taskName, e);
}
return taskContext;
}
}
/**
* {@link ArrayAdapter} implementation for displaying {@link Task}
* instances.
* @author Pixmob
*/
private static class TaskContextAdapter extends ArrayAdapter<TaskContext> {
private static final Map<TaskState, Integer> TASK_COLORS = new HashMap<TaskListFragment.TaskState, Integer>(
4);
private final LayoutInflater layoutInflater;
public TaskContextAdapter(final Context context, final TaskContext[] taskContexts) {
super(context, R.layout.task_row, taskContexts);
layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// Lazy initialize colors.
if (TASK_COLORS.isEmpty()) {
final Resources r = context.getResources();
TASK_COLORS.put(TaskState.RUNNABLE, r.getColor(R.color.task_color_runnable));
TASK_COLORS.put(TaskState.RUNNING, r.getColor(R.color.task_color_running));
TASK_COLORS.put(TaskState.FINISHED, r.getColor(R.color.task_color_finished));
TASK_COLORS.put(TaskState.FAILED, r.getColor(R.color.task_color_failed));
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final View row;
if (convertView == null) {
row = layoutInflater.inflate(R.layout.task_row, parent, false);
} else {
row = convertView;
}
final TaskContext taskContext = getItem(position);
final TextView tv = (TextView) row.findViewById(R.id.task_name);
tv.setText(taskContext.task.getName());
tv.setTextColor(TASK_COLORS.get(taskContext.state));
final ProgressBar taskProgress = (ProgressBar) row.findViewById(R.id.task_progress);
if (TaskState.RUNNING.equals(taskContext.state)) {
taskProgress.setVisibility(View.VISIBLE);
} else {
taskProgress.setVisibility(View.GONE);
}
return row;
}
}
/**
* {@link Task} context.
* @author Pixmob
*/
public static class TaskContext {
public final Task task;
public TaskState state;
public Intent userIntent;
public TaskContext(final Task task) {
this.task = task;
reset();
}
public void reset() {
state = TaskState.RUNNABLE;
userIntent = null;
}
}
/**
* {@link Task} state.
* @author Pixmob
*/
private static enum TaskState {
RUNNABLE, RUNNING, FINISHED, FAILED
}
}