Recycler chat Pagination add new message at top - java
I am working on a chat app where I need to show reverse pagination and show the user's previous chat when the user scrolls up.
I am first loading data from SQLite database to show the last 10 messages of the user.
public Cursor getLimitUserChat(String UserID,int nextChat){
Log.d(TAG,"Get Single Row Running= ID "+UserID);
database=this.getReadableDatabase();
cursor = database.rawQuery("SELECT * FROM " + TABLE_NAME + " Where "+ RECEIVER_USERID +"="+ UserID+" ORDER BY ID DESC LIMIT 10 OFFSET "+nextChat,null);
return cursor;
}
Then I add a cursor to the custom ArrayList
private class databaseAsync extends AsyncTask<Void,Void,Void> {
boolean checkDB_Exist,chatItemsCounts;
private Parcelable recyclerViewState;
#Override
protected void onPreExecute() {
super.onPreExecute();
Log.d(TAG,"Chat Database Function "+databaseChatValue);
if (databaseChatValue == 0){
message.clear();
}
recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();
//chatCursor=chat_database.getUserChat(UserID_Intent);
chatCount = chat_database.getUserChatCount(UserID_Intent);
chatCursor=chat_database.getLimitUserChat(UserID_Intent,databaseChatValue);
checkDB_Exist=functions.DatabaseExist(getActivity(),"CHAT_DATABASE.DB");
chatItemsCounts=chatCursor.getCount()>0;
chatCursor.moveToFirst();
Log.d(TAG,"Chat Cursor "+ chatCursor.getCount());
Log.d(TAG,"Total Chat Counts "+chatCount);
}
#Override
protected Void doInBackground(Void... voids) {
if (checkDB_Exist && chatCursor.getCount()>0 && chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID")).equals(UserID_Intent)) {
chatCursor.moveToFirst();
do {
database_rowID = chatCursor.getInt(chatCursor.getColumnIndex("ID"));
database_userID = chatCursor.getString(chatCursor.getColumnIndex("USER_ID"));
database_RoomName = chatCursor.getString(chatCursor.getColumnIndex("ROOM_NAME"));
database_ReceiverID = chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID"));
database_MessageType = chatCursor.getString(chatCursor.getColumnIndex("MESSAGE_TYPE"));
database_Message = chatCursor.getString(chatCursor.getColumnIndex("USER_MESSAGE"));
database_MsgFrom = chatCursor.getString(chatCursor.getColumnIndex("SENDER_NAME"));
database_MsgTo = chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_NAME"));
database_TimeStamp = chatCursor.getString(chatCursor.getColumnIndex("TIME_STAMP"));
database_FCMfrom = chatCursor.getString(chatCursor.getColumnIndex("SENDER_TOKEN"));
database_FCMto = chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_TOKEN"));
database_LocalPath = chatCursor.getString(chatCursor.getColumnIndex("DOWNLOADED_AT"));
database_PhoneFrom = chatCursor.getString(chatCursor.getColumnIndex("MY_PHONE"));
database_PhoneTo = chatCursor.getString(chatCursor.getColumnIndex("OTHER_PHONE"));
Log.d(TAG, "Value Of Database Message String = " + database_Message);
Log.d(TAG, "Row ID of Database " + database_rowID);
// Check Message Type
if (true) {
if (database_MessageType.equals("Image")) {
Log.d(TAG, "Message Type Is Image");
Log.d(TAG, "Row ID of Database " + database_rowID);
Chat_Wrapper image = new Chat_Wrapper(null, database_Message, null, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
message.add(image);
} else if (database_MessageType.equals("GIF")) {
Chat_Wrapper image = new Chat_Wrapper(null, database_Message, null, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
message.add(image);
} else if (database_MessageType.equals("Video") || (database_MessageType.equals("Youtube_Video"))) {
Log.d(TAG, "Message Type Is Video");
Chat_Wrapper Video = new Chat_Wrapper(null, null, database_Message, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
message.add(Video);
} else if (database_MessageType.equals("Audio")) {
Log.d(TAG, "Message Type Is Audio");
Chat_Wrapper Audio = new Chat_Wrapper(null, null, null, database_Message, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
message.add(Audio);
} else if (database_MessageType.equals("Documents")) {
Log.d(TAG, "Message Type Is Documents");
Chat_Wrapper Docs = new Chat_Wrapper(null, null, null, null, database_Message, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
message.add(Docs);
} else if (database_MessageType.equals("Location")) {
Log.d(TAG, "Message Type Is Location");
Chat_Wrapper Location = new Chat_Wrapper(null, null, null, null, null, database_Message, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
message.add(Location);
} else if (database_MessageType.equals("Geofence")) {
Log.d(TAG, "Message Type Is Geofence");
Chat_Wrapper GeoFence = new Chat_Wrapper(null, null, null, null, null, null, null, Chat_TimeStamp, UserPhone_Intent, UserImage_Intent, database_MsgFrom, database_Message, null, database_rowID);
message.add(GeoFence);
} else if (database_MessageType.equals("Contacts")) {
Log.d(TAG, "Message Type Is Contacts");
Chat_Wrapper Contacts = new Chat_Wrapper(null, null, null, null, null, null, database_Message, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
message.add(Contacts);
} else if (database_MessageType.equals("Online Media")) {
Log.d(TAG, "Message Type Is Online Media");
Chat_Wrapper Online_Media = new Chat_Wrapper(null, null, database_Message, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
message.add(Online_Media);
} else if (database_MessageType.equals("Text")) {
Log.d(TAG, "Message Type Is Text");
Chat_Wrapper text = new Chat_Wrapper(database_Message, null, null, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
message.add(text);
} else if (database_MessageType.equals("Google_Search")) {
Log.d(TAG, "Message Type Is Google Search");
List<String> gSearch = new ArrayList<>();
gSearch.add(database_Message);
Chat_Wrapper GoogleSearch = new Chat_Wrapper(null, null, null, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, gSearch, database_rowID);
message.add(GoogleSearch);
}
}
}
while (chatCursor.moveToNext()) ;
Room_Name_Intent = database_RoomName;
chatCursor.close();
boolean value = chat_database.isDatabaseClose();
Log.d(TAG, "Value Of Database Close or Not " + value);
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
adapter.notifyDataSetChanged();
if (message.size()>2){
recyclerView.smoothScrollToPosition(message.size()-1);
}else {
recyclerView.smoothScrollToPosition(layoutManager.getItemCount());
}
Log.d(TAG,"Chat Database "+database_OFFSET);
database_OFFSET +=10;
Log.d(TAG,"Chat Database "+database_OFFSET);
}
}
I also have made RecyclerView and LayoutManager settings like this
layoutManager.setStackFromEnd(true);
layoutManager.setReverseLayout(true);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
At first load, it fetches total 10 result from the local database but as soon as it loads into screen first, it loads in the correct order (Latest message at the end and older at the top). But RecyclerView doesn't scroll at bottom automatically. At the start, it loads from the top and when I scroll it calls the following method.
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL){
Log.d(TAG,"Hello I am scrolling screen ");
isScrolling = true;
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
currentVisible = layoutManager.getChildCount();
TotalItems = layoutManager.getItemCount();
scrolledOutItems = layoutManager.findFirstVisibleItemPosition();
int check = TotalItems - currentVisible+scrolledOutItems;
Log.d(TAG,"Current Visible = "+currentVisible+" Total = "+TotalItems+" Scrolled Out Items = "+scrolledOutItems+" Check = "+check);
if (isScrolling && TotalItems == currentVisible + scrolledOutItems ){
Log.d(TAG,"Fetch Data Now "+databaseChatValue);
if (chatCount > databaseChatValue){
Log.d(TAG,"Total item count is more than database = "+chatCount +" "+databaseChatValue);
new databaseAsync().execute();
isScrolling = false;
}
}
}
});
And loads another into screen.
Issues In Code
When Set layoutManager.setStackFromEnd(true); without layoutManager.setReverseLayout(true); pagination works at bottom of last message and also new message adds at bottom
When Set layoutManager.setReverseLayout(true); pagination works as required but new message stacked at top of the oldest message.
Update :I notice my newest message has index number in array [0] and my last message after scrolls to oldest message [21]. So when i typed new message into my chat box it assign to [22] in array and shows above oldest message at top not at the bottom of the screen.
Whenever you add new item into your chat screen(arraylist) dont go with message.add(Video); instead add item at 0 position like this message.add(0,Video); which will add items at bottom of screen(top of array list) than adding top of the screen(last of array list). It will be add into start position of array i.e. [0] in your case.
Update
Now I think, the problem is with your database query. I assume that whenever a new message is typed, it is stored in the database with the highest ID that makes it the latest in your database table organization. Hence, the getLimitUserChat should never return the latest message if you are setting the offset correctly.
I do not know what the dataBaseAsync.execute function do. I assume, it adds more elements into the ArrayList and calls the notifyDateSetChanged.
Hence the data should be populated in your RecyclerView in proper order if the addition of the newly fetched data to the ArrayList is correct. However, you might also notice that each time new paginated data is adding, your RecyclerView might scroll to the bottom as the reverse layout is enabled - which you might not want for a chat application.
Hence to keep the RecyclerView at the same place before loading new data you need to save the state of the RecyclerView and restore the state when the new data is fetched. Here is the code for storing the RecyclerView state and getting back into the same state.
// Save state
private Parcelable recyclerViewState;
recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();
// Restore state
recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
And I would like to suggest you set the LayoutManager like the following.
layoutManager.setReverseLayout(true);
layoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
Please double check if the ArrayList is populated correctly (i.e. new data at the top of the list and older data at the bottom).
Hope that helps!
Please remove the following from your onPostExecute.
if (message.size() > 2){
recyclerView.smoothScrollToPosition(message.size()-1);
} else {
recyclerView.smoothScrollToPosition(layoutManager.getItemCount());
}
And also add necessary null checkings in the preExecute.
Related
i want to display username in my project using SQLite?
in my project after user sign up , i want to display the username in next page so, this line works but with only first user sign up if another user sign up it will display the first username. displName =findViewById(R.id.displayName); displName.setText(database.getAllNote().get(0).getUserName()); my database #Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_TABLE); } #Override public void onUpgrade(SQLiteDatabase db, int i, int i1) { db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); onCreate(db); } public void openDatabase() { db = this.getWritableDatabase();} #SuppressLint("Range") public List<UserTasks> getAllNote(){ List<UserTasks> listNote = new ArrayList<>(); Cursor cur = null; db.beginTransaction(); try{ cur = db.query(TABLE_NAME, null, null, null, null, null, null, null); if(cur != null){ if(cur.moveToFirst()){ do{ UserTasks note = new UserTasks(); note.setUser_id(cur.getInt(cur.getColumnIndex(UID))); note.setUserName(cur.getString(cur.getColumnIndex(UserName))); note.setPassword(cur.getString(cur.getColumnIndex(Password))); note.setNote(cur.getString(cur.getColumnIndex(Note))); note.setStatus(cur.getInt(cur.getColumnIndex(STATUS))); note.setDate(cur.getString(cur.getColumnIndex(date))); note.setTitle(cur.getString(cur.getColumnIndex(titleChe))); note.setDecs(cur.getString(cur.getColumnIndex(desc))); note.setDay(cur.getString(cur.getColumnIndex(day))); listNote.add(note); } while(cur.moveToNext()); } } } finally { db.endTransaction(); assert cur != null; cur.close(); } return listNote; } how i can fix this small issue? pls help
You always get the first sign up user because you always retrieve table index 0 displName.setText(database.getAllNote().get(0).getUserName()); So I suggest you to use SharedPreference to increment the count of signup user. When user successfully signup you have to increase the number of user SharedPreferences pref = getApplicationContext().getSharedPreferences("MyPref", 0); Int countUser = pref.getInt("count_user", 0); // getting Integer Editor editor = pref.edit(); editor.putInt("count_user", countUser+1); editor.apply(); After that, change your displName.setText to this: SharedPreferences pref = getApplicationContext().getSharedPreferences("MyPref", 0); Int countUser = pref.getInt("count_user", 0); // getting Integer displName.setText(database.getAllNote().get(countUser).getUserName()); You will always get latest signup username.
Read Downloaded images from the "Downloads" folder from the Shared Storage in Android Emulator using MediaStore. Cursor object returns with size 0
I am looking to read a set of image files from the sdcard/download directory. I am getting content://media/external_primary/downloads as the URI string when calling if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { collection = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY); } else { collection = MediaStore.Downloads.EXTERNAL_CONTENT_URI; } I am using the Cursor interface to iterate through the directory but for some reason, my cursor is returning with a size of 0. I do not know what I am doing wrong to make this happen but below is the code that I have so far. Looking for some direction. Uri collection; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { collection = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY); } else { collection = MediaStore.Downloads.EXTERNAL_CONTENT_URI; } String[] projection = { MediaStore.MediaColumns._ID, MediaStore.MediaColumns.TITLE }; try { Cursor cursor = getApplicationContext().getContentResolver().query(collection, projection, null, null, null); // Cache column indices. int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Downloads._ID); int nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Downloads.TITLE); while (cursor.moveToNext()) { // Get values of columns for a given video. long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Downloads._ID)); String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Downloads.DISPLAY_NAME)); Uri contentUri = ContentUris.withAppendedId(MediaStore.Downloads.EXTERNAL_CONTENT_URI, id); ImageView imageView = findViewById(R.id.imageView); imageView.setImageURI(contentUri); } } catch (Exception e) { e.printStackTrace(); } Snippet to the directory structure https://i.stack.imgur.com/ELkQw.png
Obtain the same ID from the contact picker and cursor
I'm using a contact picker as such: startActivityForResult( new Intent( Intent.ACTION_PICK, Phone.CONTENT_URI ), CONTACT_PICKER_RESULT //1001 ); This type of contact picker picks out a specific phone number that a contact has. I then get an ID for this contact with: protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode != CONTACT_PICKER_RESULT || resultCode != RESULT_OK) return; String id = data.getData.getLastPathSegment(); } However, when I use a PhoneLookup query, such as: Cursor cur = getContentResolver().query( Uri.withAppendedPath( PhoneLookup.CONTENT_FILTER_URI, Uri.encode(...) //phone number of contact is filled in ), new String[] { PhoneLookup.DISPLAY_NAME, Phone._ID }, null, null, null ); if (!cur.moveToFirst()) return; String id = cur.getString( cur.getColumnIndex(Phone._ID) ); The ID I get from the PhoneLookup is different from the onActivityResult. For example, the contact picker returns 1408 while the cursor returns 444. How can I get: data.getData().getLastPathSegment() equal to cur.getColumnIndex(...)?
You can't use a PhoneLookup in this case. You need to query Phone.CONTENT_URI to obtain the ID that matches up with the contact picker. The following code below does exactly this. Cursor cur = getContentResolver().query( Phone.CONTENT_URI, new String[] { Phone.DISPLAY_NAME, Phone._ID }, Phone.NORMALIZED_NUMBER + "=?", new String[] { ... //phone number placed here }, null); if (!cur.moveToFirst()) return; String id = cur.getString(cur.getColumnIndex(Phone._ID)));
android.database.CursorIndexOutOfBoundsException...once again
So, I have this error in user crash report for my app. Here's the report: android.database.CursorIndexOutOfBoundsException: Index 0 requested, with a size of 0 at android.database.AbstractCursor.checkPosition(AbstractCursor.) at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.) at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:50) at rs.androidaplikacije.zastaveigradovi.Kviz10Hard.nextQuestionGrad(Kviz10Hard.) at rs.androidaplikacije.zastaveigradovi.Kviz10Hard.access$1(Kviz10Hard.) at rs.androidaplikacije.zastaveigradovi.Kviz10Hard$2.run(Kviz10Hard.java:68) at android.os.Handler.handleCallback(Handler.) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.) at android.app.ActivityThread.main(ActivityThread.) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.) at com.android.internal.os.ZygoteInit.main(ZygoteInit.) at dalvik.system.NativeStart.main(Native Method) And here's the mentioned code: private void nextQuestionGrad() { flag.setVisibility(View.GONE); dodatnoPitanje.setVisibility(View.VISIBLE); TestAdapter mDbHelper = new TestAdapter(this); DataBaseHelper myDbHelper = new DataBaseHelper(this); if(!myDbHelper.checkDataBase()){ mDbHelper.createDatabase(); } try{ mDbHelper.open(); Cursor c = mDbHelper.getTestDataGradovi(mCurrentID); List<Answer> labels = new ArrayList<Answer>(); labels.add(new Answer(c.getString(2), true)); labels.add(new Answer(c.getString(3), false)); labels.add(new Answer(c.getString(4), false)); labels.add(new Answer(c.getString(5), false)); tacanOdg = c.getString(2); Collections.shuffle(labels); dodatnoPitanje.setText(c.getString(1)); bOdgovor1.setText(labels.get(0).option); bOdgovor1.setTag(labels.get(0)); bOdgovor1.setOnClickListener(clickListenerGrad); bOdgovor2.setText(labels.get(1).option); bOdgovor2.setTag(labels.get(1)); bOdgovor2.setOnClickListener(clickListenerGrad); bOdgovor3.setText(labels.get(2).option); bOdgovor3.setTag(labels.get(2)); bOdgovor3.setOnClickListener(clickListenerGrad); bOdgovor4.setText(labels.get(3).option); bOdgovor4.setTag(labels.get(3)); bOdgovor4.setOnClickListener(clickListenerGrad); score.setText("Score: " + brojacTacnihOdgovora); }finally{ mDbHelper.close(); } } And part of my adapter class I'm using for this class: public Cursor getTestDataGradovi(long id){ try{ String sql ="SELECT * FROM tblGradovi WHERE _ID = " + id; Cursor c = mDb.rawQuery(sql, null); if (c!=null){ c.moveToNext(); } return c; }catch (SQLException mSQLException){ Log.e(TAG, "getTestData >>"+ mSQLException.toString()); throw mSQLException; } } Can someone tell me where's the problem? The error line (Kviz10Hard.) is here: labels.add(new Answer(c.getString(2), true));
Before trying to get any data from a Cursor object you need to iterate the result or get the first one if the cursor contains only one value. For example to iterate between all result you should do something like this : String sql = "SELECT * FROM myTable"; Cursor cursor = mDbHelper.rawQuery(sql, null); if(cursor != null){ if(cursor.getCount() > 0){ for(cursor.move(0);cursor.moveToNext();cursor.isAfterLast()){ // get your data here } } cursor.close(); // close your cursor when you don't need it anymore } or another way to do this it : String sql = "SELECT * FROM myTable"; Cursor cursor = mDbHelper.rawQuery(sql, null); if(cursor != null){ if(cursor.getCount() > 0){ do { // get value } while(cursor.moveToNext()); } cursor.close(); // close your cursor when you don't need it anymore } or if you are pretty sure that the result should be only one object depending on your sql statement you should call cursor.moveToFirst(); before accessing anything from your Cursor like this : String sql = "SELECT * FROM myTable"; Cursor cursor = mDbHelper.rawQuery(sql, null); if(cursor != null){ cursor.moveToFirst(); String myString = cursor.getString(cursor.getColumnIndex("title")); cursor.close(); // close your cursor when you don't need it anymore } Edit: If you really want to create function like this in your database class I would do something like this : public static Cursor getUserTitles(int userId){ String sql = "SELECT * FROM myTable WHERE userId=?"; return mDbHelper.rawQuery(sql, new String[]{Integer.toString(userId)}); } Second Edit: Change your getTestDataGradovi to : public Cursor getTestDataGradovi(long id){ String sql ="SELECT * FROM tblGradovi WHERE _ID=?"; return mDb.rawQuery(sql, new String[]{Integer.toString(id)}); } and add c.moveToFirst(); , after Cursor c = mDbHelper.getTestDataGradovi(mCurrentID);
You are not checking that the Cursor contains any results. You need to check the return value of c.moveToNext(), or check c.getCount() or something similar.
Invalid characters in sqlite query?
I'm having issues creating a valid sql query. Im trying to check if a value, title is present in my db. Here is the method in my adapter class that creates the query. public Cursor byTitle(String title) throws SQLException { Cursor mCursor = db.query(true, DATABASE_TABLE, new String[] { KEY_URL }, "title" + "=" +title, null, null, null, null, null); if (mCursor != null) { mCursor.moveToFirst(); } return mCursor; } And its called onClick with holder.fb.setOnClickListener(new OnClickListener() { public void onClick(View v) { final FDBAdapter db = new FDBAdapter(ctx); title = holder.itemName.getText().toString(); db.open(); Cursor c = db.byTitle(title); if (c.getCount()>0) { while (c.moveToNext()) { Toast.makeText(ctx, "yup! " + c.getString(0), Toast.LENGTH_LONG).show(); } } else { Toast.makeText(ctx, "nope! " + holder.itemName.getText().toString(), Toast.LENGTH_LONG).show(); } c.close(); db.close(); } }); My error is android.database.sqlite.SQLiteException: near "Rootz": syntax error: , while compiling: SELECT DISTINCT url FROM favs WHERE title=[Guide] Rootz Wiki's List of ROMs / Kernels / Tweaks / Theme's / Radios LTE Edition I've tried putting quotes around title like "\""+title+"\"" but that wasn't working for me either. How can I make this a valid db query?
Can you try this, might work. Cursor mCursor = db.query(true, DATABASE_TABLE, new String[] { KEY_URL }, "title=?", title, null, null, null, null);
Not familiar with SQLite, but I'd try single quotes, like this: "title" + "='" + title + "'",