Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove extra state and use Android APIs in activities with checkable lists #303 #480

Merged
merged 29 commits into from
Mar 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
3ae773d
Make mFontSizePreference a local variable.
dcbriccetti Feb 27, 2017
8ed494b
Move preference keys to a new class, PreferenceKeys. This shortens Pr…
dcbriccetti Feb 27, 2017
fff4cfb
Encapsulate the findPreference deprecation warning and allow shorter …
dcbriccetti Feb 27, 2017
b3ded2d
Move preference keys to a new class, PreferenceKeys. This shortens Pr…
dcbriccetti Feb 27, 2017
b7fc893
Merge remote-tracking branch 'remotes/upstream/master'
dcbriccetti Feb 27, 2017
a2ddbd6
Merge remote-tracking branch 'remotes/upstream/master'
dcbriccetti Feb 27, 2017
e72bd39
Merge remote-tracking branch 'upstream/master'
dcbriccetti Feb 27, 2017
796970c
PreferencesActivity: Remove duplicate code. Organize. Improve readabi…
dcbriccetti Feb 28, 2017
6227962
Further improve the code that removes disabled preferences.
dcbriccetti Feb 28, 2017
31c92c3
Fix comments.
dcbriccetti Feb 28, 2017
c0574a9
Make minor improvements to DisabledPreferencesRemover
dcbriccetti Feb 28, 2017
e28bdb8
Push up common preferences code to new abstract superclass, AppPrefer…
dcbriccetti Feb 28, 2017
7dd22ac
Merge remote-tracking branch 'upstream/master' into PreferencesActivi…
dcbriccetti Feb 28, 2017
5adc77f
Further improve the automatic removal of general preferences based on…
dcbriccetti Mar 1, 2017
71e149f
Inline a field
dcbriccetti Mar 1, 2017
2563f9d
Remove extra state and use Android APIs in activities with checkable …
dcbriccetti Mar 1, 2017
9a2ed90
Merge remote-tracking branch 'upstream/master' into 303-activities-ch…
dcbriccetti Mar 1, 2017
4c3b602
Merge remote-tracking branch 'upstream/master' into 303-activities-ch…
dcbriccetti Mar 1, 2017
7ce7075
Remove extra state and use Android APIs in activities with checkable …
dcbriccetti Mar 1, 2017
ed53604
Remove extra state and use Android APIs in activities with checkable …
dcbriccetti Mar 2, 2017
f4e3446
Discard checkbox checked state saving and restoring code, but keep th…
dcbriccetti Mar 2, 2017
e277ac5
Fix my global change error, and make Log tag objects simple static st…
dcbriccetti Mar 2, 2017
7020f7f
Move remaining ListViewUtils code to abstract base class AppListActiv…
dcbriccetti Mar 2, 2017
ce5ffd6
Simplify AppListActivity getCheckedItemInfo, now that we no longer ne…
dcbriccetti Mar 2, 2017
3250e22
Remove @NonNull. Change ++i to i++.
dcbriccetti Mar 2, 2017
ba4e5f8
Change ++i to i++.
dcbriccetti Mar 2, 2017
2865e1d
Restore the enabled state of the Select/Clear All button on configura…
dcbriccetti Mar 2, 2017
bd27bd4
Remove unused import
dcbriccetti Mar 2, 2017
cd04af1
Restore inadvertently-removed line.
dcbriccetti Mar 2, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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