-
Notifications
You must be signed in to change notification settings - Fork 15
Starred Issues #236
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
base: master
Are you sure you want to change the base?
Starred Issues #236
Changes from all commits
c526fc4
6525896
ae9bbc3
ebc67d3
02e9998
af117ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -64,6 +64,7 @@ public class IssueActivity extends AppCompatActivity { | |
| public static final String KEY_ISSUE = "key_issue"; | ||
| public static final String KEY_IS_LOW_ACCURACY = "key_is_low_accuracy"; | ||
| public static final String KEY_DONATE_IS_ON = "key_donate_is_on"; | ||
| public static final String KEY_IS_STARRED = "key_is_starred"; | ||
|
|
||
| public static final int RESULT_OK = 1; | ||
| public static final int RESULT_SERVER_ERROR = 2; | ||
|
|
@@ -80,6 +81,7 @@ public class IssueActivity extends AppCompatActivity { | |
| private boolean mIsLowAccuracy = false; | ||
| private boolean mDonateIsOn = false; | ||
| private boolean mIsAnimating = false; | ||
| private boolean mIsStarred = false; | ||
|
|
||
| private ActivityIssueBinding binding; | ||
|
|
||
|
|
@@ -96,12 +98,14 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { | |
| } | ||
| mIsLowAccuracy = getIntent().getBooleanExtra(KEY_IS_LOW_ACCURACY, false); | ||
| mDonateIsOn = getIntent().getBooleanExtra(KEY_DONATE_IS_ON, false); | ||
| mIsStarred = getIntent().getBooleanExtra(KEY_IS_STARRED, false); | ||
|
|
||
| setContentView(binding.getRoot()); | ||
|
|
||
| if (getSupportActionBar() != null) { | ||
| getSupportActionBar().setDisplayHomeAsUpEnabled(true); | ||
| getSupportActionBar().setTitle(mIssue.name); | ||
| // getSupportActionBar().setTitle(mIssue.name); | ||
brericha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| getSupportActionBar().setDisplayShowTitleEnabled(false); | ||
| } | ||
|
|
||
| binding.issueName.setText(mIssue.name); | ||
|
|
@@ -202,6 +206,7 @@ protected void onSaveInstanceState(Bundle outState) { | |
| super.onSaveInstanceState(outState); | ||
| outState.putParcelable(KEY_ISSUE, mIssue); | ||
| outState.putBoolean(KEY_IS_LOW_ACCURACY, mIsLowAccuracy); | ||
| outState.putBoolean(KEY_IS_STARRED, mIsStarred); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we annotate |
||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -272,6 +277,16 @@ public void onClick(View view) { | |
| public boolean onCreateOptionsMenu(Menu menu) { | ||
| MenuInflater inflater = getMenuInflater(); | ||
| inflater.inflate(R.menu.menu_issue, menu); | ||
|
|
||
| MenuItem starItem = menu.findItem(R.id.menu_star); | ||
| if (mIsStarred) { | ||
| starItem.setIcon(R.drawable.ic_star_white_24dp); | ||
| starItem.setTitle(R.string.action_unstar_issue); | ||
| } else { | ||
| starItem.setIcon(R.drawable.ic_star_outline_white_24dp); | ||
| starItem.setTitle(R.string.action_star_issue); | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
|
|
@@ -285,6 +300,17 @@ public boolean onOptionsItemSelected(MenuItem item) { | |
| sendShare(); | ||
| return true; | ||
| } | ||
| if (item.getItemId() == R.id.menu_star) { | ||
| DatabaseHelper db = AppSingleton.getInstance(getApplicationContext()).getDatabaseHelper(); | ||
| if (mIsStarred) { | ||
| db.removeStarredIssue(mIssue.id); | ||
| mIsStarred = false; | ||
| } else { | ||
| db.addStarredIssue(mIssue.id); | ||
| mIsStarred = true; | ||
| } | ||
| invalidateOptionsMenu(); | ||
| } | ||
| if (item.getItemId() == R.id.menu_details) { | ||
| showIssueDetails(); | ||
| return true; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,6 +54,7 @@ | |
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Objects; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| import static android.view.View.VISIBLE; | ||
|
|
||
|
|
@@ -86,6 +87,7 @@ public class MainActivity extends AppCompatActivity implements IssuesAdapter.Cal | |
| private boolean mShowLowAccuracyWarning = true; | ||
| private boolean mDonateIsOn = false; | ||
| private FirebaseAuth mAuth = null; | ||
| private List<String> starredIssues; | ||
|
|
||
| private ActivityMainBinding binding; | ||
|
|
||
|
|
@@ -207,7 +209,7 @@ public void onError() { | |
| binding.searchText.setText(mSearchText); | ||
| } | ||
| } else { | ||
| // Safe to use index as the top two filters are hard-coded strings. | ||
| // Safe to use index as the top three filters are hard-coded strings. | ||
| mFilterText = mFilterAdapter.getItem(0); | ||
| } | ||
| binding.searchText.setOnClickListener(new View.OnClickListener() { | ||
|
|
@@ -279,6 +281,7 @@ public void onGlobalLayout() { | |
| }); | ||
|
|
||
| loadStats(); | ||
| loadStarredIssues(); | ||
|
|
||
| mAddress = accountManager.getAddress(this); | ||
| mLatitude = accountManager.getLat(this); | ||
|
|
@@ -352,6 +355,7 @@ public void startIssueActivity(Context context, Issue issue) { | |
| issueIntent.putExtra(RepCallActivity.KEY_LOCATION_NAME, mLocationName); | ||
| issueIntent.putExtra(IssueActivity.KEY_IS_LOW_ACCURACY, mIsLowAccuracy); | ||
| issueIntent.putExtra(IssueActivity.KEY_DONATE_IS_ON, mDonateIsOn); | ||
| issueIntent.putExtra(IssueActivity.KEY_IS_STARRED, starredIssues.contains(issue.id)); | ||
| startActivityForResult(issueIntent, ISSUE_DETAIL_REQUEST); | ||
| } | ||
|
|
||
|
|
@@ -428,6 +432,12 @@ public void onIssuesReceived(List<Issue> issues) { | |
| mIssuesAdapter.setAllIssues(issues, IssuesAdapter.NO_ERROR); | ||
| mIssuesAdapter.setFilterAndSearch(mFilterText, mSearchText); | ||
| binding.swipeContainer.setRefreshing(false); | ||
|
|
||
| List<String> ids = new ArrayList<>(); | ||
| for (Issue i : issues) { | ||
| ids.add(i.id); | ||
| } | ||
| AppSingleton.getInstance(getApplicationContext()).getDatabaseHelper().trimStarredIssues(ids); | ||
| } | ||
| }; | ||
|
|
||
|
|
@@ -555,7 +565,7 @@ private void updateOnBackPressedCallbackEnabled() { | |
| } | ||
|
|
||
| private void populateFilterAdapterIfNeeded(List<Issue> issues) { | ||
| if (mFilterAdapter.getCount() > 2) { | ||
| if (mFilterAdapter.getCount() > 3) { | ||
| // Already populated. Don't try again. | ||
| // This assumes that the categories won't change much during the course of a session. | ||
| return; | ||
|
|
@@ -609,6 +619,13 @@ private void loadStats() { | |
| } | ||
| } | ||
|
|
||
| private void loadStarredIssues() { | ||
| starredIssues = AppSingleton.getInstance(getApplicationContext()) | ||
| .getDatabaseHelper().getStarredIssues(); | ||
| Log.d(TAG, starredIssues.toString()); | ||
| mIssuesAdapter.setStarredIssues(starredIssues); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the DBHelper file, I suggested:
I suggest that getting starred issues from an existing list might also be handy because it means we won't have to call notifyDataSetChanged, instead we can just update our mIssues and notifyItemChanged for each issue that is starred. In other words, this class will do a lot less work and we'll won't need to redraw the issues list a second time. |
||
| } | ||
|
|
||
| private void showStats() { | ||
| Intent intent = new Intent(this, StatsActivity.class); | ||
| startActivity(intent); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,13 +22,15 @@ | |
| public class DatabaseHelper extends SQLiteOpenHelper { | ||
| private static final String TAG = "DatabaseHelper"; | ||
|
|
||
| private static final int DATABASE_VERSION = 3; | ||
| private static final int DATABASE_VERSION = 4; | ||
| @VisibleForTesting | ||
| protected static final String CALLS_TABLE_NAME = "UserCallsDatabase"; | ||
| @VisibleForTesting | ||
| protected static final String ISSUES_TABLE_NAME = "UserIssuesTable"; | ||
| @VisibleForTesting | ||
| protected static final String CONTACTS_TABLE_NAME = "UserContactsTable"; | ||
| @VisibleForTesting | ||
| protected static final String STARRED_ISSUES_TABLE_NAME = "StarredIssuesTable"; | ||
|
|
||
| // Can be used to control time in tests. | ||
| private TimeProvider mTimeProvider; | ||
|
|
@@ -82,6 +84,15 @@ public static class ContactColumns { | |
| "CREATE TABLE " + CONTACTS_TABLE_NAME + " (" + ContactColumns.CONTACT_ID + " STRING, " + | ||
| ContactColumns.CONTACT_NAME + " STRING);"; | ||
|
|
||
| private static class StarredIssuesColumns { | ||
| public static String ID = "issueid"; | ||
| public static String TIMESTAMP = "timestamp"; | ||
| } | ||
|
|
||
| private static final String STARRED_ISSUES_TABLE_CREATE = | ||
| "CREATE TABLE " + STARRED_ISSUES_TABLE_NAME + " (" + StarredIssuesColumns.ID + " STRING, " | ||
| + StarredIssuesColumns.TIMESTAMP + " INTEGER);"; | ||
|
|
||
| public DatabaseHelper(Context context) { | ||
| this(context, new DefaultTimeProvider()); | ||
| } | ||
|
|
@@ -96,6 +107,7 @@ public void onCreate(SQLiteDatabase db) { | |
| db.execSQL(CALLS_TABLE_CREATE); | ||
| db.execSQL(ISSUES_TABLE_CREATE); | ||
| db.execSQL(CONTACTS_TABLE_CREATE); | ||
| db.execSQL(STARRED_ISSUES_TABLE_CREATE); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -124,6 +136,10 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { | |
| new String[]{Outcome.Status.VM.toString()}); | ||
| currentDbVersion = 3; | ||
| } | ||
|
|
||
| if (oldVersion < 4 && currentDbVersion < newVersion) { | ||
| db.execSQL(STARRED_ISSUES_TABLE_CREATE); | ||
brericha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -164,6 +180,21 @@ private void addContact(String contactId, String contactName) { | |
| SQLiteDatabase.CONFLICT_IGNORE); | ||
| } | ||
|
|
||
| public void addStarredIssue(String issueId) { | ||
| ContentValues values = new ContentValues(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please add a test that adding the same issue twice doesn't cause trouble. |
||
| values.put(StarredIssuesColumns.ID, issueId); | ||
| values.put(StarredIssuesColumns.TIMESTAMP, mTimeProvider.currentTimeMillis()); | ||
| getWritableDatabase().insert(STARRED_ISSUES_TABLE_NAME, null, values); | ||
| } | ||
|
|
||
| public void removeStarredIssue(String issueId) { | ||
| getWritableDatabase().delete( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please add a test that there's no issue if we remove an issue that's not in in the starred issues DB. |
||
| STARRED_ISSUES_TABLE_NAME, | ||
| StarredIssuesColumns.ID + " = ?", | ||
| List.of(issueId).toArray(new String[0]) | ||
| ); | ||
| } | ||
|
|
||
| public String getIssueName(String issueId) { | ||
| Cursor c = getReadableDatabase().rawQuery("SELECT " + IssuesColumns.ISSUE_NAME + " FROM " + | ||
| ISSUES_TABLE_NAME + " WHERE " + IssuesColumns.ISSUE_ID + " = '" + issueId + "'", | ||
|
|
@@ -333,6 +364,37 @@ public List<Pair<String, Integer>> getCallCountsByIssue() { | |
| return result; | ||
| } | ||
|
|
||
| public List<String> getStarredIssues() { | ||
| Cursor c = getReadableDatabase().rawQuery( | ||
| "SELECT " + StarredIssuesColumns.ID + " FROM " + STARRED_ISSUES_TABLE_NAME | ||
| + " ORDER BY " + StarredIssuesColumns.TIMESTAMP, null); | ||
| List<String> result = new ArrayList<>(); | ||
| while (c.moveToNext()) { | ||
| result.add(c.getString(0)); | ||
| } | ||
| c.close(); | ||
| return result; | ||
| } | ||
|
|
||
| // Delete any starred issues not present in the provided list of issue ids to prevent the | ||
| // table from filling up with issues that don't exist any more so the app doesn't have to look | ||
| // through as many items when seeing if the issue needs to be marked as starred | ||
| public int trimStarredIssues(List<String> ids) { | ||
brericha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| StringBuilder placeholders = new StringBuilder(); | ||
| for (int i = 0; i < ids.size(); i++) { | ||
| placeholders.append("?"); | ||
| if (i < ids.size() - 1) { | ||
| placeholders.append(", "); | ||
| } | ||
| } | ||
|
|
||
| String whereClause = StarredIssuesColumns.ID + " NOT IN (" | ||
| + placeholders + ")"; | ||
| String[] whereArgs = ids.toArray(new String[0]); | ||
|
|
||
| return getWritableDatabase().delete(STARRED_ISSUES_TABLE_NAME, whereClause, whereArgs); | ||
| } | ||
|
|
||
| @VisibleForTesting | ||
| public static String sanitizeContactId(String contactId) { | ||
| // TODO this only works on single quotes and not double quotes. Triple quotes are still | ||
|
|
@@ -342,4 +404,6 @@ public static String sanitizeContactId(String contactId) { | |
| } | ||
| return contactId; | ||
| } | ||
|
|
||
|
|
||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should be able to add the star as a svg without having to add all these png assets in different directories -- it'll keep the app binary size from increasing as much and reduce overhead for us if we change it in the future. You can download the star from here: https://fonts.google.com/icons?selected=Material+Symbols+Outlined:star:FILL@0;wght@400;GRAD@0;opsz@24&icon.size=24&icon.color=%23e8eaed To fill it I think you can make an XML using that SVG and then set the |
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This icon isn't high enough contrast. Can we make the star one of the app colors, like dark blue or red or bright blue instead? |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -477,9 +477,13 @@ | |
| <!-- Filter for showing all issues [CHAR_LIMIT=50] --> | ||
| <string name="all_issues_filter">All issues</string> | ||
|
|
||
| <!-- Filter for showing starred issue [CHAR_LIMIT=50] --> | ||
| <string name="starred_issues_filter">Starred issues</string> | ||
|
|
||
| <string-array name="default_filters"> | ||
| <item>@string/top_issues_filter</item> | ||
| <item>@string/all_issues_filter</item> | ||
| <item>@string/starred_issues_filter</item> | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we hide and/or disable this unless the user has at least 1 starred issue in the current list? |
||
| </string-array> | ||
|
|
||
| <!-- Title of a section in settings about notifications [CHAR_LIMIT=50] --> | ||
|
|
@@ -635,4 +639,9 @@ | |
| <!-- The categories an issue belongs to, when there are more than one. --> | ||
| <string name="issue_category_many">Categories: </string> | ||
|
|
||
| <!-- Action bar icon tooltip for starring an issue --> | ||
| <string name="action_star_issue">Star Issue</string> | ||
brericha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| <!-- Action bar icon tooltip for un-starring an issue --> | ||
| <string name="action_unstar_issue">Unstar Issue</string> | ||
| </resources> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it might be easier to annotate
mIssueswithisStarredrather than having a second list ofstarredIssueIds? Or have a starredIssueIds be aSet<String> mStarredIssuesfor easy lookups.