/*
* Copyright (c) 2013, Will Szumski
* Copyright (c) 2013, Doug Szumski
*
* This file is part of Cyclismo.
*
* Cyclismo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cyclismo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cyclismo. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright 2008 Google Inc.
*
* 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.cowboycoders.cyclismo;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.speech.tts.TextToSpeech;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.ResourceCursorAdapter;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import org.cowboycoders.cyclismo.content.CyclismoProviderUtils;
import org.cowboycoders.cyclismo.content.MyTracksProviderUtils;
import org.cowboycoders.cyclismo.content.User;
import org.cowboycoders.cyclismo.content.UserInfoColumns;
import org.cowboycoders.cyclismo.fragments.ConfirmationDialogFragment;
import org.cowboycoders.cyclismo.fragments.ConfirmationDialogFragment.DialogCallback;
import org.cowboycoders.cyclismo.util.ApiAdapterFactory;
import org.cowboycoders.cyclismo.util.IntentUtils;
import org.cowboycoders.cyclismo.util.PreferencesUtils;
import org.cowboycoders.cyclismo.util.SettingsUtils;
/**
* An activity displaying a list of user.
*
* @author Will Szumski
*/
public class UserListActivity extends FragmentActivity {
public static final String TAG = UserListActivity.class.getSimpleName();
private static final String[] PROJECTION = new String[] { UserInfoColumns._ID,
UserInfoColumns.NAME };
// protected static final int COURSE_SETUP_RESPONSE_CODE = 0;
/*
* Note that sharedPreferenceChangeListenr cannot be an anonymous inner class.
* Anonymous inner class will get garbage collected.
*/
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (key != null) {
if (key.equals(getString(R.string.settings_select_user_current_selection_key))) {
Log.i(TAG," current user changed");
updateCancelButton();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
updateMenuItems(isRecording);
resourceCursorAdapter.notifyDataSetChanged();
}
});
}
}
};
// Callback when an item is selected in the contextual action mode
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
@Override
public boolean onClick(int itemId, int position, long id) {
return handleContextItem(itemId, position);
}
};
// The following are set in onCreate
private SharedPreferences sharedPreferences;
private ListView listView;
private ResourceCursorAdapter resourceCursorAdapter;
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
// Menu items
private MenuItem deleteAllMenuItem;
private CyclismoProviderUtils providerUtils;
private Button cancelButton;
private Handler mHandler;
@SuppressWarnings("unused")
private ContextMenu contextMenu;
public static final class DeleteUserCallback implements DialogCallback, Parcelable {
private User user;
public DeleteUserCallback(Parcel in) {
user = in.readParcelable(User.class.getClassLoader());
}
public DeleteUserCallback(User user) {
this.user = user;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(user,flags);
}
@Override
public void onConfirm(final Context context) {
final CyclismoProviderUtils providerUtils = MyTracksProviderUtils.Factory.getCyclimso(context);
// don't run on ui thread
new Thread() {
public void run() {
long userId = user.getId();
providerUtils.deleteUser(user.getId());
long currentUser = PreferencesUtils.getLong(context, R.string.settings_select_user_current_selection_key);
// if deleting current user, set to invalid user
if (userId == currentUser) {
PreferencesUtils.setLong(context, R.string.settings_select_user_current_selection_key, -1L);
}
}
}.start();
}
public static final Parcelable.Creator<DialogCallback> CREATOR = new Parcelable.Creator<DialogCallback>() {
public DialogCallback createFromParcel(Parcel in) {
return new DeleteUserCallback(in);
}
public DialogCallback[] newArray(int size) {
return new DialogCallback[size];
}
};
@Override
public CharSequence getConfirmationMessage(Context context) {
String unformated = context.getString(R.string.user_list_delete_user_confirmation_message);
return String.format(unformated, user.getName());
}
@Override
public void onFinish(Context context) {
Intent intent = IntentUtils.newIntent(context, UserListActivity.class);
context.startActivity(intent);
}
};
public static final class DeleteAllUsersCallback implements DialogCallback,Parcelable {
public DeleteAllUsersCallback(@SuppressWarnings("unused") Parcel in) {
}
public DeleteAllUsersCallback() {
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
}
@Override
public void onConfirm(final Context context) {
final CyclismoProviderUtils providerUtils = MyTracksProviderUtils.Factory.getCyclimso(context);
// don't run on ui thread
new Thread() {
public void run() {
providerUtils.deleteAllUsers();
PreferencesUtils.setLong(context, R.string.settings_select_user_current_selection_key, -1L);
}
}.start();
}
public static final Parcelable.Creator<DialogCallback> CREATOR = new Parcelable.Creator<DialogCallback>() {
public DialogCallback createFromParcel(Parcel in) {
return new DeleteAllUsersCallback(in);
}
public DialogCallback[] newArray(int size) {
return new DialogCallback[size];
}
};
@Override
public CharSequence getConfirmationMessage(Context context) {
return context.getString(R.string.user_list_delete_all_users_confirmation_message);
}
@Override
public void onFinish(Context context) {
Intent intent = IntentUtils.newIntent(context, UserListActivity.class);
context.startActivity(intent);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
setContentView(R.layout.generic_list_select);
mHandler = new Handler();
this.providerUtils = MyTracksProviderUtils.Factory.getCyclimso(this);
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
listView = (ListView) findViewById(R.id.generic_list);
this.setTitle(this.getString(R.string.my_tracks_app_name) + " : "
+ this.getString(R.string.user_list_title));
TextView userEmptyListText = (TextView) findViewById(R.id.generic_list_empty_view);
userEmptyListText.setText(R.string.user_list_empty_message);
this.cancelButton = (Button) this.findViewById(R.id.generic_list_left_button);
cancelButton.setText(getString(R.string.cancel));
initCancelButton();
Button addUserButton = (Button) this.findViewById(R.id.generic_list_right_button);
addUserButton.setText(getString(R.string.user_create));
addUserButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = IntentUtils.newIntent(UserListActivity.this, UserEditActivity.class)
.putExtra(UserEditActivity.EXTRA_NEW_USER, true);
intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
// intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
// intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
});
listView.setEmptyView(userEmptyListText);
resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.user_list_item, null, 0) {
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = super.newView(context, cursor, parent);
return view;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
final User user = providerUtils.createUser(cursor, false);
TextView userName = (TextView) view.findViewById(R.id.list_item_name);
userName.setText(user.getName());
userName.setEnabled(!isRecording());
userName.setVisibility(View.VISIBLE);
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
if (isRecording) {
onUnableToPerformOperation();
} else {
// new Thread() {
// public void run() {
// PreferencesUtils.setLong(UserListActivity.this, R.string.settings_select_user_current_selection_key, user.getId());
// }
//
// }.start();
//
// Intent intent = IntentUtils.newIntent(UserListActivity.this,
// TrackListActivity.class);
// intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
// intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
// intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// startActivity(intent);
// String unformated = getString(R.string.user_list_new_user_selected);
// Toast.makeText(UserListActivity.this, String.format(unformated, user.getName()), Toast.LENGTH_SHORT).show();
// UserListActivity.this.finish();
getSyncSettingsTask().execute(user.getId());
}
}
});
registerForContextMenu(view);
}
};
listView.setAdapter(resourceCursorAdapter);
ApiAdapterFactory.getApiAdapter().configureListViewContextualMenu(this, listView,
contextualActionModeCallback, R.menu.user_list_context_menu);
getSupportLoaderManager().initLoader(0, null, new LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
return new CursorLoader(UserListActivity.this, UserInfoColumns.CONTENT_URI, PROJECTION,
null, null, "LOWER(" + UserInfoColumns.NAME + ")");
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
resourceCursorAdapter.swapCursor(cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
resourceCursorAdapter.swapCursor(null);
}
});
updateCancelButton();
this.contextMenu = (ContextMenu) findViewById(R.menu.user_list_context_menu);
}
private User loadUser(long id) {
return MyTracksProviderUtils.Factory.getCyclimso(UserListActivity.this).getUser(id);
}
private AsyncTask<Object, Integer, User> getSyncSettingsTask() {
AsyncTask<Object, Integer, User> task = new AsyncTask<Object, Integer, User>() {
@Override
protected User doInBackground(Object... params) {
Long newId = (Long) params[0];
if (newId == -1L) {
return null;
}
final long oldId = PreferencesUtils.getLong(UserListActivity.this,
R.string.settings_select_user_current_selection_key);
PreferencesUtils.setLong(UserListActivity.this, R.string.settings_select_user_current_selection_key, newId);
// save previous users settings
if (oldId != -1L) {
User user = loadUser(oldId);
SettingsUtils.saveSettings(UserListActivity.this, user);
}
User user = loadUser(newId);
SettingsUtils.restoreSettings(UserListActivity.this, user);
return user;
}
@Override
protected void onPostExecute(User result) {
Context context = UserListActivity.this;
UserListActivity.this.finish();
if (result == null) {
Intent intent = IntentUtils.newIntent(context, UserListActivity.class);
UserListActivity.this.startActivity(intent);
} else {
Intent intent = IntentUtils.newIntent(UserListActivity.this,
TrackListActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
String unformated = getString(R.string.user_list_new_user_selected);
Toast.makeText(UserListActivity.this, String.format(unformated, result.getName()), Toast.LENGTH_SHORT).show();
}
}
};
return task;
}
private void initCancelButton() {
cancelButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
doFinish();
}
});
}
private void doFinish() {
UserListActivity.this.finish();
}
private void updateCancelButton() {
// default to disable whilst we do async call
cancelButton.setEnabled(false);
// disable cancel if no users exist in database
new Thread() {
public void run() {
final long currentUser = PreferencesUtils.getLong(UserListActivity.this, R.string.settings_select_user_current_selection_key);
final boolean enable = currentUser != -1L && providerUtils.getUser(currentUser) != null;
mHandler.post(new Runnable() {
@Override
public void run() {
cancelButton.setEnabled(enable);
}
});
}
}.start();
}
@Override
protected void onStart() {
super.onStart();
// Register shared preferences listener
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
// Update shared preferences
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
}
@Override
protected void onResume() {
super.onResume();
// Update UI
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
updateMenuItems(isRecording);
resourceCursorAdapter.notifyDataSetChanged();
}
@Override
protected void onPause() {
super.onPause();
}
@Override
protected void onStop() {
super.onStop();
// Unregister shared preferences listener
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.user_list, menu);
deleteAllMenuItem = menu.findItem(R.id.user_list_delete_all);
updateMenuItems(isRecording());
return true;
}
private boolean isRecording() {
return recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
switch (item.getItemId()) {
case R.id.user_list_delete_all:
if (isRecording) {
this.onUnableToPerformOperation();
return true;
}
ConfirmationDialogFragment.newInstance(new DeleteAllUsersCallback()).show(
getSupportFragmentManager(),
ConfirmationDialogFragment.TAG);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
getMenuInflater().inflate(R.menu.user_list_context_menu, menu);
MenuItem deleteMenuItem = menu.findItem(R.id.list_context_menu_delete);
deleteMenuItem.setVisible(isRecording());
MenuItem editMenuItem = menu.findItem(R.id.list_context_menu_edit);
editMenuItem.setVisible(isRecording());
}
@Override
public boolean onContextItemSelected(MenuItem item) {
// ((AdapterContextMenuInfo) item.getMenuInfo()).position)
if (item != null
&& handleContextItem(item.getItemId(),
((AdapterContextMenuInfo) item.getMenuInfo()).position)) {
return true;
}
return super.onContextItemSelected(item);
}
/**
* Updates the menu items.
*
* @param isRecording true if recording
*/
private void updateMenuItems(boolean isRecording) {
if (deleteAllMenuItem != null) {
deleteAllMenuItem.setVisible(!isRecording);
}
}
/**
* Handles a context item selection.
*
* @param itemId the menu item id
* @return true if handled.
*/
private boolean handleContextItem(int itemId, int position) {
Intent intent;
//listView.getItemAtPosition(position);
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
User user = providerUtils.createUser((Cursor) listView.getItemAtPosition(position), false);
switch (itemId) {
case R.id.list_context_menu_edit:
intent = IntentUtils.newIntent(this, UserEditActivity.class).putExtra(
UserEditActivity.EXTRA_USER, user);
startActivity(intent);
return true;
case R.id.list_context_menu_delete:
if (isRecording) {
this.onUnableToPerformOperation();
return true;
}
ConfirmationDialogFragment.newInstance(new DeleteUserCallback(user)).show(
getSupportFragmentManager(),
ConfirmationDialogFragment.TAG);
return true;
default:
return false;
}
}
protected void onUnableToPerformOperation() {
Context context = getApplicationContext();
CharSequence text = getString(0);
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
}