Skip to content

Commit

Permalink
Merge pull request getodk#480 from dcbriccetti/303-activities-checkab…
Browse files Browse the repository at this point in the history
…le-lists

getodk#303, Remove extra state and use Android APIs in activities with checkable lists
getodk#441, Refactor preference classes
  • Loading branch information
lognaturel authored Mar 2, 2017
2 parents b4a3587 + 1293e24 commit c700214
Show file tree
Hide file tree
Showing 17 changed files with 867 additions and 902 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright 2017 Nafundi
*
* 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.odk.collect.android.activities;

import android.app.ListActivity;
import android.widget.Button;
import android.widget.ListView;

import org.odk.collect.android.R;
import org.odk.collect.android.application.Collect;
import org.odk.collect.android.database.ActivityLogger;

abstract class AppListActivity extends ListActivity {
protected final ActivityLogger logger = Collect.getInstance().getActivityLogger();

protected boolean areCheckedItems() {
return getCheckedCount() > 0;
}

/** Returns the IDs of the checked items, using the ListView of this activity. */
protected long[] getCheckedIds() {
return getCheckedIds(getListView());
}

/** Returns the IDs of the checked items, using the ListView provided */
protected long[] getCheckedIds(ListView lv) {
// This method could be simplified by using getCheckedItemIds, if one ensured that
// IDs were “stable” (see the getCheckedItemIds doc).
int itemCount = lv.getCount();
int checkedItemCount = lv.getCheckedItemCount();
long[] checkedIds = new long[checkedItemCount];
int resultIndex = 0;
for (int posIdx = 0; posIdx < itemCount; posIdx++) {
if (lv.isItemChecked(posIdx)) {
checkedIds [resultIndex] = lv.getItemIdAtPosition(posIdx);
resultIndex++;
}
}
return checkedIds;
}

/** Returns the IDs of the checked items, as an array of Long */
protected Long[] getCheckedIdObjects() {
long[] checkedIds = getCheckedIds();
Long[] checkedIdObjects = new Long[checkedIds.length];
for (int i = 0; i < checkedIds.length; i++) {
checkedIdObjects[i] = checkedIds[i];
}
return checkedIdObjects;
}

protected int getCheckedCount() {
return getListView().getCheckedItemCount();
}

// toggles to all checked or all unchecked
// returns:
// true if result is all checked
// false if result is all unchecked
//
// Toggle behavior is as follows:
// if ANY items are unchecked, check them all
// if ALL items are checked, uncheck them all
public static boolean toggleChecked(ListView lv) {
// shortcut null case
if (lv == null) return false;

boolean newCheckState = lv.getCount() > lv.getCheckedItemCount();
setAllToCheckedState(lv, newCheckState);
return newCheckState;
}

public static void setAllToCheckedState(ListView lv, boolean check) {
// no-op if ListView null
if (lv == null) return;

for (int x = 0; x < lv.getCount(); x++) {
lv.setItemChecked(x, check);
}
}

// Function to toggle button label
public static void toggleButtonLabel(Button mToggleButton, ListView lv) {
if (lv.getCheckedItemCount() != lv.getCount()) {
mToggleButton.setText(R.string.select_all);
} else {
mToggleButton.setText(R.string.clear_all);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
package org.odk.collect.android.activities;

import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.DialogInterface;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
Expand All @@ -29,14 +27,10 @@
import android.widget.Toast;

import org.odk.collect.android.R;
import org.odk.collect.android.application.Collect;
import org.odk.collect.android.dao.InstancesDao;
import org.odk.collect.android.listeners.DeleteInstancesListener;
import org.odk.collect.android.provider.InstanceProviderAPI.InstanceColumns;
import org.odk.collect.android.tasks.DeleteInstancesTask;
import org.odk.collect.android.utilities.ListViewUtils;

import java.util.ArrayList;

/**
* Responsible for displaying and deleting all the saved form instances
Expand All @@ -45,20 +39,14 @@
* @author Carl Hartung (carlhartung@gmail.com)
* @author Yaw Anokwa (yanokwa@gmail.com)
*/
public class DataManagerList extends ListActivity implements
DeleteInstancesListener {
public class DataManagerList extends AppListActivity implements DeleteInstancesListener {
private static final String t = "DataManagerList";
private AlertDialog mAlertDialog;
private Button mDeleteButton;
private Button mToggleButton;

private SimpleCursorAdapter mInstances;
private ArrayList<Long> mSelected = new ArrayList<Long>();

DeleteInstancesTask mDeleteInstancesTask = null;

private static final String SELECTED = "selected";

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Expand All @@ -69,9 +57,9 @@ public void onCreate(Bundle savedInstanceState) {
mDeleteButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Collect.getInstance().getActivityLogger().logAction(this, "deleteButton",
Integer.toString(mSelected.size()));
if (mSelected.size() > 0) {
int checkedItemCount = getCheckedCount();
logger.logAction(this, "deleteButton", Integer.toString(checkedItemCount));
if (checkedItemCount > 0) {
createDeleteInstancesDialog();
} else {
Toast.makeText(getApplicationContext(),
Expand All @@ -85,17 +73,9 @@ public void onClick(View v) {
@Override
public void onClick(View v) {
ListView lv = getListView();
boolean allChecked = ListViewUtils.toggleChecked(lv);
ListViewUtils.toggleButtonLabel(mToggleButton, getListView());
boolean allChecked = toggleChecked(lv);
toggleButtonLabel(mToggleButton, getListView());

// sync up internal state
mSelected.clear();
if (allChecked) {
// add all id's back to mSelected
for (int pos = 0; pos < lv.getCount(); pos++) {
mSelected.add(getListAdapter().getItemId(pos));
}
}
mDeleteButton.setEnabled(allChecked);
}
});
Expand All @@ -104,9 +84,9 @@ public void onClick(View v) {
InstanceColumns.DISPLAY_SUBTEXT};
int[] view = new int[]{R.id.text1, R.id.text2};

mInstances = new SimpleCursorAdapter(this,
SimpleCursorAdapter cursorAdapter = new SimpleCursorAdapter(this,
R.layout.two_item_multiple_choice, new InstancesDao().getSavedInstancesCursor(), data, view);
setListAdapter(mInstances);
setListAdapter(cursorAdapter);
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
getListView().setItemsCanFocus(false);
mDeleteButton.setEnabled(false);
Expand All @@ -121,12 +101,12 @@ public void onClick(View v) {
@Override
protected void onStart() {
super.onStart();
Collect.getInstance().getActivityLogger().logOnStart(this);
logger.logOnStart(this);
}

@Override
protected void onStop() {
Collect.getInstance().getActivityLogger().logOnStop(this);
logger.logOnStop(this);
super.onStop();
}

Expand All @@ -137,23 +117,10 @@ public Object onRetainNonConfigurationInstance() {
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
long[] selectedArray = savedInstanceState.getLongArray(SELECTED);
for (int i = 0; i < selectedArray.length; i++) {
mSelected.add(selectedArray[i]);
}
mDeleteButton.setEnabled(selectedArray.length > 0);
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
long[] selectedArray = new long[mSelected.size()];
for (int i = 0; i < mSelected.size(); i++) {
selectedArray[i] = mSelected.get(i);
}
outState.putLongArray(SELECTED, selectedArray);
protected void onRestoreInstanceState(Bundle bundle) {
Log.d(t, "onRestoreInstanceState");
super.onRestoreInstanceState(bundle);
mDeleteButton.setEnabled(areCheckedItems());
}

@Override
Expand Down Expand Up @@ -185,28 +152,28 @@ protected void onPause() {
* Create the instance delete dialog
*/
private void createDeleteInstancesDialog() {
Collect.getInstance().getActivityLogger().logAction(this, "createDeleteInstancesDialog",
logger.logAction(this, "createDeleteInstancesDialog",
"show");

mAlertDialog = new AlertDialog.Builder(this).create();
mAlertDialog.setTitle(getString(R.string.delete_file));
mAlertDialog.setMessage(getString(R.string.delete_confirm,
String.valueOf(mSelected.size())));
String.valueOf(getCheckedCount())));
DialogInterface.OnClickListener dialogYesNoListener =
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
switch (i) {
case DialogInterface.BUTTON_POSITIVE: // delete
Collect.getInstance().getActivityLogger().logAction(this,
logger.logAction(this,
"createDeleteInstancesDialog", "delete");
deleteSelectedInstances();
if (getListView().getCount() == mSelected.size()) {
if (getListView().getCount() == getCheckedCount()) {
mToggleButton.setEnabled(false);
}
break;
case DialogInterface.BUTTON_NEGATIVE: // do nothing
Collect.getInstance().getActivityLogger().logAction(this,
logger.logAction(this,
"createDeleteInstancesDialog", "cancel");
break;
}
Expand All @@ -229,58 +196,44 @@ private void deleteSelectedInstances() {
mDeleteInstancesTask = new DeleteInstancesTask();
mDeleteInstancesTask.setContentResolver(getContentResolver());
mDeleteInstancesTask.setDeleteListener(this);
mDeleteInstancesTask.execute(mSelected.toArray(new Long[mSelected
.size()]));
mDeleteInstancesTask.execute(getCheckedIdObjects());
} else {
Toast.makeText(this, getString(R.string.file_delete_in_progress),
Toast.LENGTH_LONG).show();
}
}

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);

// get row id from db
Cursor c = (Cursor) getListAdapter().getItem(position);
long k = c.getLong(c.getColumnIndex(InstanceColumns._ID));

// add/remove from selected list
if (mSelected.contains(k)) {
mSelected.remove(k);
} else {
mSelected.add(k);
}

Collect.getInstance().getActivityLogger().logAction(this, "onListItemClick",
Long.toString(k));

ListViewUtils.toggleButtonLabel(mToggleButton, getListView());
mDeleteButton.setEnabled(mSelected.size() > 0);
protected void onListItemClick(ListView l, View v, int position, long rowId) {
super.onListItemClick(l, v, position, rowId);
logger.logAction(this, "onListItemClick", Long.toString(rowId));
toggleButtonLabel(mToggleButton, getListView());
mDeleteButton.setEnabled(areCheckedItems());
}

@Override
public void deleteComplete(int deletedInstances) {
Log.i(t, "Delete instances complete");
Collect.getInstance().getActivityLogger().logAction(this, "deleteComplete",
logger.logAction(this, "deleteComplete",
Integer.toString(deletedInstances));
if (deletedInstances == mSelected.size()) {
final int checkedCount = getCheckedCount();
if (deletedInstances == checkedCount) {
// all deletes were successful
Toast.makeText(this,
getString(R.string.file_deleted_ok, String.valueOf(deletedInstances)),
Toast.LENGTH_SHORT).show();
} else {
// had some failures
Log.e(t, "Failed to delete "
+ (mSelected.size() - deletedInstances) + " instances");
+ (checkedCount - deletedInstances) + " instances");
Toast.makeText(
this,
getString(R.string.file_deleted_error, String.valueOf(mSelected.size()
- deletedInstances), String.valueOf(mSelected.size())),
getString(R.string.file_deleted_error,
String.valueOf(checkedCount - deletedInstances),
String.valueOf(checkedCount)),
Toast.LENGTH_LONG).show();
}
mDeleteInstancesTask = null;
mSelected.clear();
getListView().clearChoices(); // doesn't unset the checkboxes
for (int i = 0; i < getListView().getCount(); ++i) {
getListView().setItemChecked(i, false);
Expand Down
Loading

0 comments on commit c700214

Please sign in to comment.