I have a problem with my SQLiteOpenHelper class.
I have a database with printer manufacturers and details of any kind of printer.
I try to get all manufacturer from my database with this code and returning them in a arraylist.
// Read database for All printer manufacturers and return a list
public ArrayList<String> getPrManufacturer(){
ArrayList<String> manufacturerList = new ArrayList<String>();
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(CoDeskContract.Printer.TABLE_NAME,
printerManuProjection, null, null, null, null, null,null);
// If cursor is not null and manufacturer List
// does not contains Manufacturer in the list then add it!
if ((cursor != null) && (cursor.getCount()>0)){
cursor.moveToFirst();
do {
String cursorManufacturer = cursor.getString(0);
//Checking for manufacturer in the list
for(String manufacturerInList:manufacturerList){
if (!manufacturerInList.equals(cursorManufacturer))
manufacturerList.add(cursorManufacturer);
}
}while(cursor.moveToNext());
}
// Return list of manufacturers from database
return manufacturerList;
}
I want every manufacturer to be once in a list.
Somehow i cant to get it to work.
Im still a newbie.
Thanks for any Help.
You can also use the distinct keyword in SQLite (http://www.sqlite.org/lang_select.html). Use SQLiteDatabase.rawQuery(String query, String[] args) for that.
db.rawQuery("SELECT DISTINCT name FROM " + CoDeskContract.Printer.TABLE_NAME,null);
There are two issues:
In the beginning, when your list manufacturerInList is empty then it will not go inside for(String manufacturerInList:manufacturerList){ loop and hence it will never add any entry in the list.
Once you fix your problem 1, still it will not work as if (!manufacturerInList.equals(cursorManufacturer)) checks against each entry in the list and adds the non matching entry in the list possibly multiple times.
To fix the issue, you have two options.
Option1: Use contains as:
if (!manufacturerList.contains(cursorManufacturer)) {
manufacturerList.add(cursorManufacturer);
}
Option2: Use a matchFound boolean flag as:
String cursorManufacturer = cursor.getString(0);
boolean matchFound = false;
//Checking for manufacturer in the list
for(String manufacturerInList:manufacturerList){
if (manufacturerInList.equals(cursorManufacturer)){
matchFound = true;
break;
}
}
if(!matchFound){ // <- doesn't exit in the list
manufacturerList.add(cursorManufacturer);
}
Use ArrayList.contains(Object elem) to check if item is exist in ArrayList or not Change your code as:
// does not contains Manufacturer in the list then add it!
if ((cursor != null) && (cursor.getCount()>0)){
cursor.moveToFirst();
do {
String cursorManufacturer = cursor.getString(0);
//Checking for manufacturer in the list
if (!manufacturerList.contains(cursorManufacturer)) {
manufacturerList.add(cursorManufacturer);
} else {
System.out.println("cursorManufacturernot found");
}
}while(cursor.moveToNext());
}
Related
Before i used an ArrayList for this, but because of duplicate Album issues (which i resolved before API29 by using DISTINCT and GROUP BY statements) which are not allowed anymore to use inside a query.
I got values like this in my RecyclerViews Adapter: myArrayList.get(position).getAlbum();
But now that i'm using a HashMap, how can i get a value by position which i get from the adapter?
Code for adding values in Hashmap
HashMap<Integer, Album> albums = new HashMap<>();
String[] projection2 = { MediaStore.Audio.Media.ALBUM_ID,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.IS_MUSIC};
String selection = MediaStore.Audio.Media.IS_MUSIC + "!=0";
String sort = MediaStore.Audio.Media.ALBUM + " COLLATE NOCASE ASC";
cursor = resolver.query(musicUri, projection2, selection, null, sort);
try {
if (cursor != null) {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
int columnAlbumId = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID);
int columnAlbumName = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM);
String albumId = cursor.getString(columnAlbumId);
String albumName = cursor.getString(columnAlbumName);
if(albums.containsKey(albumId) == false){
Album album = new Album(albumId, albumName);
albums.put(albumId, album);
}
cursor.moveToNext();
}
}
}catch (Exception e){
Log.e(TAG, "Exception caught when creating ALBUM!", e);
throw new Exception();
}
By definition the HashMap is not sorted so how about a SortedMap? An implementation of a SortedMap is a TreeMap which sorts the entries according to keys (or a Comparator).
I believe that suits you since your map has integers as keys.
Edit:
You can use a List or whatever collection suits you in your adapter. For example keep a reference to the ids and the albums which is the data that you're interested in displaying through the adapter.
private int[] ids;
private Album[] albums;
As you're passing the data (included in a map) to your adapter then you could extract that data and place it in the array containers so you can take advantage of the index. For example,
public MyAdapter(Map<Integer,Album> data){
ids = new int[map.size()];
albums = new Album[map.size()];
int i = 0;
for (Map.Entry<Integer,Album> e : map.entrySet()){
ids[i] = e.getKey();
albums[i++] = e.getValue();
}
}
Now that you have your arrays you can sort them also if you like and in case you want to grab the 3rd album and its id all you need to do is,
int id = ids[2];
Album album = albums[2];
I'm retrieving list of distinct folders list having video files with number of videos in each folder, and this is working fine in devices having Android P and below, but when I run on devices having Android Q the app crashes.
How can I make it work for devices running Android Q
java.lang.IllegalArgumentException: Invalid column DISTINCT
bucket_display_name
Logcat:
java.lang.IllegalArgumentException: Invalid column DISTINCT bucket_display_name
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:170)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:140)
at android.content.ContentProviderProxy.query(ContentProviderNative.java:423)
at android.content.ContentResolver.query(ContentResolver.java:944)
at android.content.ContentResolver.query(ContentResolver.java:880)
at android.content.ContentResolver.query(ContentResolver.java:836)
at com.aisar.mediaplayer.fragments.VideoFolderFragment$MediaQuery.getAllVideo(VideoFolderFragment.java:364)
at com.aisar.mediaplayer.fragments.VideoFolderFragment$VideosLoader.loadVideos(VideoFolderFragment.java:434)
at com.aisar.mediaplayer.fragments.VideoFolderFragment$VideosLoader.access$1100(VideoFolderFragment.java:413)
at com.aisar.mediaplayer.fragments.VideoFolderFragment$5.run(VideoFolderFragment.java:189)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:289)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
My Code:
public class MediaQuery {
private Context context;
private int count = 0;
private Cursor cursor;
List<ModelVideoFolder> videoItems;
public MediaQuery(Context context) {
this.context = context;
}
public List<ModelVideoFolder> getAllVideo(String query) {
String selection = null;
String[] projection = {
"DISTINCT " + MediaStore.Video.Media.BUCKET_DISPLAY_NAME,
MediaStore.Video.Media.BUCKET_ID
};
cursor = context.getContentResolver().query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
null,
query);
videoItems = new ArrayList<>();
ModelVideoFolder videoItem;
while (cursor.moveToNext()) {
videoItem = new ModelVideoFolder(
"" + cursor.getString(1),
"" + cursor.getString(0),
"",
"",
"" + getVideosCount(cursor.getString(1))
);
videoItems.add(videoItem);
}
return videoItems;
}
public int getVideosCount(String BUCKET_ID) {
int count = 0;
String selection = null;
String[] projection = {
MediaStore.Video.Media.BUCKET_ID,
};
Cursor cursor = getActivity().getContentResolver().query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
null,
null);
while (cursor.moveToNext()) {
if (BUCKET_ID.equals(cursor.getString(0))) {
//add only those videos that are in selected/chosen folder
count++;
}
}
return count;
}
}
This is due to the restrictions in Android Q.
In Android Q the projection must contain only valid column names without additional statements. Is not possible anymore to embed any type of SQL statement in the projection.
So, projections such as "DISTINCT " + YourColumName, or even trying to make a column alias such as "ExistingColumnName AS AnotherName" will always fail.
The workaround is to perform multiple queries (cursors) to get your required metrics, and construct with the results a CursorWrapper or MatrixCursor.
See the next issue link, where is stated this behavior as expected, since is part of the improved storage security model in Q:
https://issuetracker.google.com/issues/130965914
For your specific problem, a solution could be as next:
First query for a cursor to obtain the list of the BUCKET_ID values where all the videos are located. In the selection you can filter to target only video files by using MediaStore.Files.FileColumns.MEDIA_TYPE = MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO
With the retrieved cursor, iterate all the BUCKET_ID values to perform individual queries per bucket and retrieve the video records, from which you can resolve the count. While iterating keep track of each BUCKET_ID and skip any already queried. And don't forget to also perform the same MEDIA_TYPE filter selection, to avoid querying none-video files that may reside in the same bucket.
Try the next snippet based in your question code, I haven't test it but you may get an idea about how to proceed:
public static class MediaQuery
{
#NonNull
public static HashMap<String, ModelVideoFolder> get(#NonNull final Context context)
{
final HashMap<String, ModelVideoFolder> output = new HashMap<>();
final Uri contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
final String[] projection = {MediaStore.Video.Media.BUCKET_DISPLAY_NAME,
MediaStore.Video.Media.BUCKET_ID};
try (final Cursor cursor = context.getContentResolver().query(contentUri,
projection, null, null, null))
{
if ((cursor != null) && (cursor.moveToFirst() == true))
{
final int columnBucketName = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.BUCKET_DISPLAY_NAME);
final int columnBucketId = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.BUCKET_ID);
do
{
final String bucketName = cursor.getString(columnBucketName);
final String bucketId = cursor.getString(columnBucketId);
if (output.containsKey(bucketId) == false)
{
final int count = MediaQuery.getCount(context, contentUri, bucketId);
final ModelVideoFolder item = new ModelVideoFolder(
bucketName, bucketId, null, null, count);
output.put(bucketId, item);
}
} while (cursor.moveToNext());
}
}
return output;
}
private static int getCount(#NonNull final Context context, #NonNull final Uri contentUri,
#NonNull final String bucketId)
{
try (final Cursor cursor = context.getContentResolver().query(contentUri,
null, MediaStore.Video.Media.BUCKET_ID + "=?", new String[]{bucketId}, null))
{
return ((cursor == null) || (cursor.moveToFirst() == false)) ? 0 : cursor.getCount();
}
}
}
The DISTINCT keyword actually belongs to the SELECT statement, not to a column. For example SELECT DISTINCT Country, Name FROM CountriesTable. Therefore adding DISTINCT to a column projection is a hack which randomly worked in the previous Android versions and probably stopped working in Android 10 due to some changes. Since the ContentResolver doesn't allow raw queries, you just have to filter unique folders inside your code, e. g. by using a HashSet.
I was facing the same problem. DISTINCT keyword doesn't work in Android 10, use hashset for distinct.
I try to check if contact is favorite(starred) in Android. Here what I do:
String starred = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.STARRED));
Boolean isFavorite;
if(starred=="1"){
isFavorite = true;
}else{
isFavorite = false;
}
My isFavorite always returns false, even if starred returns 1.
What is wrong with my code?
public void checkFavorite(){
Cursor getContactNumber = getContentResolver().query(
ContactsContract.Data.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.NUMBER+" LIKE ?",
new String[] { "%"+number }, //number is your number to check
null);
if(getContactNumber != null) {
while (getContactNumber.moveToNext()) {
String favcheck = getContactNumber.getString(getContactNumber.getColumnIndex(ContactsContract.CommonDataKinds.Phone.STARRED));
}
if(favcheck.equals("1")){
// contact is fav
}else{
// contact is not fav
}
}
}
You need to check your result with equals method. If you do it with ==, you try to check if two variables assignment to the same object. And obviously "1" and starred are different objects.
Try this:
if ("1".equals(starred) {
I am stuck with a problem in which some contacts contain a country code while others don't.
I have code for fetching contact numbers which are being saved in list. The list looks like this:
[9999999999, 91-8888888888, 91-0000000000, 1111111111]
Now, I want to remove country code from those numbers which have it. I can't figure out a way to check this.
Here is my code for fetching contacts:
ContentResolver cr = getApplicationContext().getContentResolver(); //Activity/Application android.content.Context
Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, "UPPER(" + ContactsContract.Contacts.DISPLAY_NAME + ") ASC");
if(cursor.moveToFirst())
{
do
{
String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
if(Integer.parseInt(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0)
{
Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",new String[]{ id }, null);
while (pCur.moveToNext())
{
String contactName = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY));
String contactNumber = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
allContacts.add(contactName);
contactNumber = contactNumber.replaceAll("\\s+","");
allnumbers.add(contactNumber);
break;
}
pCur.close();
}
} while (cursor.moveToNext()) ;
}
}
Here, allnumbers contains the number (where some have country code with them and others don't). How can I remove the country code from those who have it?
Any help or keywords to search will be appreciated, thanks.
If you want to remove the first occurance of a string containing '91-', you could simply use the replaceFirst() method. So your code will end up like this:
contactNumber.replaceAll("\\s+","").replaceFirst("91-","");
If you want to check if a string starts with '91-', you could use this:
if (contactNumber.startsWith("91-")) {
}
In your sample with array of numbers you have dash(-) between countrycode. So you can just use indexOf to determine if number has country code. You can do the same thing using regexp([\d]+-[\d]+)
Also you can count degits. Usually numbers without country codes have only 10 digits. It depends on your situation
Just try
contactNumber = contactNumber.replace("+91", "");
Hope this helps...
I created a small activity that displays all contacts with phone numbers in my phone. However, there are duplicates for contacts that have whatsapp installed. For example, if John is in my contact list and he has a whatsapp account as well, the list would look like this:
...
Jake
John
John
JP
...
This is my code for assigning the cursor to the the adapter which links to a listview.
Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
String sortOrder = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC";
final Cursor cursor = getContentResolver().query(uri, null, null, null, sortOrder);
String[] from = {ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER, ContactsContract.CommonDataKinds.Phone._ID};
int[] to = {android.R.id.text1};
adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_multiple_choice, cursor, from, to, 0);
EDIT
With this code, I confirmed that the duplicates have 0 value for the ContactsContract.CommonDataKinds.Phone.TYPE which means it is a custom contact (Whatsapp). The rest are 2 which means it is a normal contact.
I need to figure out a query where it doesn't use any contacts where ContactsContract.CommonDataKinds.Phone.TYPE == 0
I know it might be a bit late. However I was having the same issue.
ContactsContract.CommonDataKinds.Phone.TYPE as per the android documentation refers not to whether it is a custom contact but to what type of contact it is i.e. Mobile(2), Home(1) or Work(3). Whatsapp contacts will be a 2 - mobile.
I used the following function to remove the duplicates (not sure if there is a better way):
private boolean checkDuplicate(String position) {
LinkedHashMap<String, Integer> map;
Integer testNull;
map=new LinkedHashMap<>();
testNull=map.get(position);
if(testNull==null) {
testNull=1;
map.put(position, testNull);
return false;
}
else {
testNull=testNull+1;
if(testNull==2) {
return true;
}
else {
map.put(position, testNull);
return false;
}
}
}