ProgressDialogue with DictionaryDatabase - android

I am implementing DictionaryDatabase in andriod. At that time i have an EditText View in which i type a word and matching word comes from the database. Now problem is that my database is too large.Like for Word "A" i have from 200 to 300 words. At first time on installation of app the database have to be loaded which takes 1 to 3 minutes and its horrible. Now i want that on after app installation or on click on EditTex means on focus i should call an asyntask with ProgressDialogue box. Do loadWords in database and on completion dismiss the dialogue. I know about async task but i don't know how to use async for my that code
here is the code for Database Loading Class:
/**
* Contains logic to return specific words from the dictionary, and
* load the dictionary table when it needs to be created.
*/
public class DictionaryDatabase extends SQLiteOpenHelper {
private static final String TAG = "DictionaryDatabase";
final static String DB_DESTINATION = "/data/data/YOUR_PACKAGE_NAME/databases/dictionary.db";
private DictionaryDatabase mDictionary;
//****FOr Dictionary****//
static int [] raw_textFiles={R.raw.a,R.raw.b,R.raw.c,
R.raw.d,R.raw.e,R.raw.f,
R.raw.g,R.raw.h,R.raw.i,
R.raw.j,R.raw.k,R.raw.l,
R.raw.m,R.raw.n,R.raw.o,
R.raw.p,R.raw.q,R.raw.r,
R.raw.s,R.raw.t,R.raw.u,
R.raw.v,R.raw.w,R.raw.x,
R.raw.y,R.raw.z};
//Array for Parsing Use
ArrayList<String> Words = new ArrayList<String>();
private static String word;
private final Context mHelperContext;
private SQLiteDatabase mDatabase;
private ProgressDialog dialog;
//The columns we'll include in the dictionary table
public static final String KEY_WORD = SearchManager.SUGGEST_COLUMN_TEXT_1;
public static final String KEY_DEFINITION = SearchManager.SUGGEST_COLUMN_TEXT_2;
private static final String DATABASE_NAME = "dictionary";
private static final String FTS_VIRTUAL_TABLE = "FTSdictionary";
private static final int DATABASE_VERSION = 2;
private static final HashMap<String,String> mColumnMap = buildColumnMap();
private static final String FTS_TABLE_CREATE =
"CREATE VIRTUAL TABLE " + FTS_VIRTUAL_TABLE +
" USING fts3 (" +
KEY_WORD + ", " +
KEY_DEFINITION + ");";
/**
* Constructor
* #param context The Context within which to work, used to create the DB
*/
public DictionaryDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mHelperContext = context;
}
/**
* Builds a map for all columns that may be requested, which will be given to the
* SQLiteQueryBuilder. This is a good way to define aliases for column names, but must include
* all columns, even if the value is the key. This allows the ContentProvider to request
* columns w/o the need to know real column names and create the alias itself.
*/
private static HashMap<String,String> buildColumnMap() {
HashMap<String,String> map = new HashMap<String,String>();
map.put(KEY_WORD, KEY_WORD);
map.put(KEY_DEFINITION, KEY_DEFINITION);
map.put(BaseColumns._ID, "rowid AS " +
BaseColumns._ID);
map.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, "rowid AS " +
SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
map.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, "rowid AS " +
SearchManager.SUGGEST_COLUMN_SHORTCUT_ID);
return map;
}
/**
* Returns a Cursor positioned at the word specified by rowId
*
* #param rowId id of word to retrieve
* #param columns The columns to include, if null then all are included
* #return Cursor positioned to matching word, or null if not found.
*/
public Cursor getWord(String rowId, String[] columns) {
String selection = "rowid = ?";
String[] selectionArgs = new String[] {rowId};
return query(selection, selectionArgs, columns);
/* This builds a query that looks like:
* SELECT <columns> FROM <table> WHERE rowid = <rowId>
*/
}
/**
* Returns a Cursor over all words that match the given query
*
* #param query The string to search for
* #param columns The columns to include, if null then all are included
* #return Cursor over all words that match, or null if none found.
*/
public Cursor getWordMatches(String query, String[] columns) {
String selection = KEY_WORD + " MATCH ?";
String[] selectionArgs = new String[] {query+"*"};
return query(selection, selectionArgs, columns);
/* This builds a query that looks like:
* SELECT <columns> FROM <table> WHERE <KEY_WORD> MATCH 'query*'
* which is an FTS3 search for the query text (plus a wildcard) inside the word column.
*
* - "rowid" is the unique id for all rows but we need this value for the "_id" column in
* order for the Adapters to work, so the columns need to make "_id" an alias for "rowid"
* - "rowid" also needs to be used by the SUGGEST_COLUMN_INTENT_DATA alias in order
* for suggestions to carry the proper intent data.
* These aliases are defined in the DictionaryProvider when queries are made.
* - This can be revised to also search the definition text with FTS3 by changing
* the selection clause to use FTS_VIRTUAL_TABLE instead of KEY_WORD (to search across
* the entire table, but sorting the relevance could be difficult.
*/
}
/**
* Performs a database query.
* #param selection The selection clause
* #param selectionArgs Selection arguments for "?" components in the selection
* #param columns The columns to return
* #return A Cursor over all rows matching the query
*/
private Cursor query(String selection, String[] selectionArgs, String[] columns) {
/* The SQLiteBuilder provides a map for all possible columns requested to
* actual columns in the database, creating a simple column alias mechanism
* by which the ContentProvider does not need to know the real column names
*/
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables(FTS_VIRTUAL_TABLE);
builder.setProjectionMap(mColumnMap);
Cursor cursor = builder.query(getReadableDatabase(),
columns, selection, selectionArgs, null, null, null);
if (cursor == null) {
return null;
} else if (!cursor.moveToFirst()) {
cursor.close();
return null;
}
return cursor;
}
/**
* This creates/opens the database.
*/
#Override
public void onCreate(SQLiteDatabase db) {
mDatabase = db;
Log.i("PATH",""+mDatabase.getPath());
mDatabase.execSQL(FTS_TABLE_CREATE);
// loadDictionary();
}
/**
* Starts a thread to load the database table with words
*/
private void loadDictionary() {
new Thread(new Runnable() {
public void run() {
try {
loadWords();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}).start();
}
public void loadWords() throws IOException {
Log.d(TAG, "Loading words...");
for(int i=0;i<=25;i++)
{ //***//
final Resources resources = mHelperContext.getResources();
InputStream inputStream = resources.openRawResource(raw_textFiles[i]);
//InputStream inputStream = resources.openRawResource(R.raw.definitions);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
try {
StringBuilder sb = new StringBuilder();
while ((word = reader.readLine()) != null)
{
sb.append(word);
// Log.i("WORD in Parser", ""+word);
}
String contents = sb.toString();
StringTokenizer st = new StringTokenizer(contents, "||");
while (st.hasMoreElements()) {
String row = st.nextElement().toString();
String title = row.substring(0, row.indexOf("$$$"));
String desc = row.substring(row.indexOf("$$$") + 3);
// Log.i("Strings in Database",""+title+""+desc);
long id = addWord(title,desc);
if (id < 0) {
Log.e(TAG, "unable to add word: " + title);
}
}
} finally {
reader.close();
}
} //***//
Log.d(TAG, "DONE loading words.");
}
/**
* Add a word to the dictionary.
* #return rowId or -1 if failed
*/
public long addWord(String word, String definition) {
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_WORD, word);
initialValues.put(KEY_DEFINITION, definition);
return mDatabase.insert(FTS_VIRTUAL_TABLE, null, initialValues);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS " + FTS_VIRTUAL_TABLE);
onCreate(db);
}
}
UPDATED:
EditTEx search;
//::::::::::::::::::::::TextWatcher For EditTextView :::::::::::::::::::::://
TextWatcher myTextWatcher = new TextWatcher() {
#SuppressLint("NewApi")
#Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
query= (search.getText().toString());
char character = 0;
if(query.isEmpty()==false)
{
character=query.toLowerCase().charAt(0);
}
if (start==0) {
//Log.i("query",""+query);
Cursor cursor = managedQuery(DictionaryProvider.CONTENT_URI, null, null,new String[] {query}, null);
viewFlipper.showNext();
}
listAdapter.notifyDataSetChanged();
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
#SuppressLint("NewApi")
#Override
public void afterTextChanged(Editable s) {
query= s.toString();
char character = 0;
if(query.isEmpty()==false)
{
character=query.toLowerCase().charAt(0);
}
Cursor cursor = managedQuery(DictionaryProvider.CONTENT_URI, null, null,new String[] {query}, null);
listAdapter.getFilter().filter(s);
}
};
Here is Provider Class:
*/
private static UriMatcher buildUriMatcher() {
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
// to get definitions...
matcher.addURI(AUTHORITY, "dictionary", SEARCH_WORDS);
matcher.addURI(AUTHORITY, "dictionary/#", GET_WORD);
// to get suggestions...
matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);
/* The following are unused in this implementation, but if we include
* {#link SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} as a column in our suggestions table, we
* could expect to receive refresh queries when a shortcutted suggestion is displayed in
* Quick Search Box, in which case, the following Uris would be provided and we
* would return a cursor with a single item representing the refreshed suggestion data.
*/
matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT, REFRESH_SHORTCUT);
matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*", REFRESH_SHORTCUT);
return matcher;
}
#Override
public boolean onCreate() {
mDictionary = new DictionaryDatabase(getContext());
return true;
}
/**
* Handles all the dictionary searches and suggestion queries from the Search Manager.
* When requesting a specific word, the uri alone is required.
* When searching all of the dictionary for matches, the selectionArgs argument must carry
* the search query as the first element.
* All other arguments are ignored.
*/
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// Use the UriMatcher to see what kind of query we have and format the db query accordingly
switch (sURIMatcher.match(uri)) {
case SEARCH_SUGGEST:
if (selectionArgs == null) {
throw new IllegalArgumentException(
"selectionArgs must be provided for the Uri: " + uri);
}
return getSuggestions(selectionArgs[0]);
case SEARCH_WORDS:
if (selectionArgs == null) {
throw new IllegalArgumentException(
"selectionArgs must be provided for the Uri: " + uri);
}
return search(selectionArgs[0]);
case GET_WORD:
return getWord(uri);
case REFRESH_SHORTCUT:
return refreshShortcut(uri);
default:
throw new IllegalArgumentException("Unknown Uri: " + uri);
}
}
private Cursor getSuggestions(String query) {
query = query.toLowerCase();
String[] columns = new String[] {
BaseColumns._ID,
DictionaryDatabase.KEY_WORD,
DictionaryDatabase.KEY_DEFINITION,
/* SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
(only if you want to refresh shortcuts) */
SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID};
return mDictionary.getWordMatches(query, columns);
}
private Cursor search(String query) {
query = query.toLowerCase();
String[] columns = new String[] {
BaseColumns._ID,
DictionaryDatabase.KEY_WORD,
DictionaryDatabase.KEY_DEFINITION};
return mDictionary.getWordMatches(query, columns);
}
private Cursor getWord(Uri uri) {
String rowId = uri.getLastPathSegment();
String[] columns = new String[] {
DictionaryDatabase.KEY_WORD,
DictionaryDatabase.KEY_DEFINITION};
return mDictionary.getWord(rowId, columns);
}
private Cursor refreshShortcut(Uri uri) {
/* This won't be called with the current implementation, but if we include
* {#link SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} as a column in our suggestions table, we
* could expect to receive refresh queries when a shortcutted suggestion is displayed in
* Quick Search Box. In which case, this method will query the table for the specific
* word, using the given item Uri and provide all the columns originally provided with the
* suggestion query.
*/
String rowId = uri.getLastPathSegment();
String[] columns = new String[] {
BaseColumns._ID,
DictionaryDatabase.KEY_WORD,
DictionaryDatabase.KEY_DEFINITION,
SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID};
return mDictionary.getWord(rowId, columns);
}
/**
* This method is required in order to query the supported types.
* It's also useful in our own query() method to determine the type of Uri received.
*/
#Override
public String getType(Uri uri) {
switch (sURIMatcher.match(uri)) {
case SEARCH_WORDS:
return WORDS_MIME_TYPE;
case GET_WORD:
return DEFINITION_MIME_TYPE;
case SEARCH_SUGGEST:
return SearchManager.SUGGEST_MIME_TYPE;
case REFRESH_SHORTCUT:
return SearchManager.SHORTCUT_MIME_TYPE;
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
}

There is a number of tutorials like
http://www.javasrilankansupport.com/2012/11/asynctask-android-example-asynctask-in.html
The problem is that the life cycle of an AsyncTask differs from the Activity one: by the moment that AsyncTask completes, the Activity may be already removed from the screen and replaced by another Activity. An activity is re-created e.g. when the screen orientation changes. But it is the old, not visible anymore, Activity that gets notified by the AsyncTask about operation completion.
See the discussion at:
Is AsyncTask really conceptually flawed or am I just missing something?
(Note the incompatibility between versions in the default AsyncTask behaviour: prior to API 11, different AsyncTasks were executed in parallel; since API 11, they are executed serially on a single worker thread.)
The solution is to invoke long operations from Model (in MVC sense) rather than from Controller (=Activity), and this most likely will not be an AsyncTask.
That being said, some code:
public class AsyncTaskTestActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
Log.d("~~~~","~~~onCreate ~~~ "+this);
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void onStartButton(View view) {
Log.d("~~~~","~~~onStartButton {");
class MyTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
Log.d("~~~~","~~~doInBackground started");
try {
for (int i=0; i<10; i++) {
Log.d("~~~~","~~~sleep#"+i);
Thread.sleep(200);
}
Log.d("~~~~","~~~sleeping over");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.d("~~~~","~~~doInBackground ended");
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
taskDone();
}
}
MyTask task = new MyTask();
task.execute(null);
Log.d("~~~~","~~~onStartButton }");
}
private void taskDone() {
Log.d("~~~~","\n\n~~~taskDone ~~~ "+this+"\n\n");
}
public void onStopButton(View view) {
Log.d("~~~~","~~~onStopButton {");
MagicAppRestart.doRestart(this);
Log.d("~~~~","~~~onStopButton }");
}
public void onPause() { Log.d("~~~~","~~~onPause ~~~ "+this); super.onPause(); }
public void onStop() { Log.d("~~~~","~~~onStop ~~~ "+this); super.onPause(); }
public void onDestroy() { Log.d("~~~~","~~~onDestroy ~~~ "+this); super.onDestroy(); }
}
Basically, you have to refactor your app so that arrival of the AsyncTask data is an asynchronous event. And if an operation is unacceptably long, it will not become faster in an AsyncTask.

Related

Data Deleted From Recycler List But Not From Sqlite Database

I am working on android application I which I am using Recycler Adapter and Sqlite database to delete and update the sqlite database.
My Problem is when I Click on Item in Recycler view to delete It Deletes the data from list, But When I Click Back and open List Again The deleted data is still there.
I have checked my database From device File Explorer That The Data does not deleted from database.
Same thing happens with the Update
Here is My Recycler Adapter Class
public class UserRecyclerAdapterSavedUsers extends RecyclerView.Adapter<UserRecyclerAdapterSavedUsers.UserViewHolder> {
private List<User> listUsers;
Context mContext;
RecyclerView mRecyclerView;
ItemClickListenerLongPressed itemClickListenerLongPressed;
UserRecyclerAdapterSavedUsers userRecyclerAdapterSavedUsers;
View itemView;
public UserRecyclerAdapterSavedUsers(List<User> listUsers,RecyclerView recyclerView) {
this.listUsers = listUsers;
mRecyclerView=recyclerView;
}
#Override
public UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
mContext= parent.getContext();
itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_user_recycler_second, parent, false);
return new UserViewHolder(itemView);
}
/**
* ViewHolder class
*/
public class UserViewHolder extends RecyclerView.ViewHolder {
//public AppCompatTextView ID;
public AppCompatTextView textViewID;
public AppCompatTextView textViewName;
public AppCompatTextView textViewPassword;
public AppCompatTextView textViewRole;
LinearLayout layout;
public UserViewHolder(View view) {
super(view);
textViewID = (AppCompatTextView) view.findViewById(R.id.textViewID);
textViewName = (AppCompatTextView) view.findViewById(R.id.textViewName);
textViewPassword = (AppCompatTextView) view.findViewById(R.id.textViewPassword);
textViewRole = (AppCompatTextView) view.findViewById(R.id.textViewRole);
layout = (LinearLayout) view.findViewById(R.id.list_view);
}
}
#Override
public void onBindViewHolder(UserViewHolder holder, final int position) {
holder.textViewID.setText(listUsers.get(position).getUserid());
holder.textViewName.setText(listUsers.get(position).getName());
holder.textViewPassword.setText(listUsers.get(position).getPassword());
holder.textViewRole.setText(listUsers.get(position).getRole());
holder.layout.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
displayingAlertDialog(position);
return false;
}
});
}
public void setItemClickListenerLongPressed(ItemClickListenerLongPressed itemClickListenerLongPressed) {
this.itemClickListenerLongPressed = itemClickListenerLongPressed;
}
#Override
public int getItemCount() {
Log.v(UsersRecyclerAdapter.class.getSimpleName(),""+listUsers.size());
return listUsers.size();
}
private void displayingAlertDialog(final int position) {
final User user= new User();
//displaying alert dialog box
AlertDialog.Builder builder = new AlertDialog.Builder(itemView.getContext());
builder.setTitle("Choose Option");
builder.setMessage("Update or Delete?");
builder.setPositiveButton("Update", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//go to update activity
gotupdateuserActivity(user.getUserid());
// dialog.cancel();
}
});
builder.setNeutralButton("Delete", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//go to Remove Item
DatabaseHelper dbHelper = new DatabaseHelper(mContext);
dbHelper.deletePersonRecord(user.getUserid(), mContext);
listUsers.remove( position);
notifyItemRemoved(position);
mRecyclerView.removeViewAt(position);
notifyItemRangeChanged(position, listUsers.size());
notifyDataSetChanged();
dialog.cancel();
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
AlertDialog alert11 = builder.create();
alert11.show();
}
public void remove(int position) {
listUsers.remove(position);
notifyItemRemoved(position);
}
private void gotupdateuserActivity(String userid) {
Intent goToUpdate = new Intent(mContext, UpdateUserRec.class);
goToUpdate.putExtra("USER_ID", userid);
Toast.makeText(mContext, "USER REC", Toast.LENGTH_SHORT).show();
mContext.startActivity(goToUpdate);
}
}
Here is Sqlite Database Helper Class
public class DatabaseHelper extends SQLiteOpenHelper {
// Database Version
private static final int DATABASE_VERSION = 2;
Context context;
// Database Name
private static final String DATABASE_NAME = "DynamicERP.db";
public static final String table_imei = "IMEITABLE";
public static final String table_login= "USERLOGIN";
// User Table Columns names
public static final String imeiid = "IMEIID";
public static final String imei = "IMEI";
public static final String userid = "USERID";
public static final String username = "USERNAME";
public static final String password = "PASSWORD";
public static final String userrole = "USERROLE";
// create table sql query
private static final String DATABASE_CIMEI = "CREATE TABLE " + table_imei + "("
+ imeiid + " INTEGER, " + imei + " VARCHAR );" ;
private static final String DATABASE_CUSER = "CREATE TABLE " + table_login + "("
+ userid + " INTEGER, " + username + " VARCHAR, " + password + " INTEGER, " + userrole + " VARCHAR );" ;
// drop table sql query
private String DROP_IMEI_TABLE = "DROP TABLE IF EXISTS " + table_imei;
private String DROP_USER_TABLE = "DROP TABLE IF EXISTS " + table_login;
/**
* Constructor
*
* #param context
*/
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CIMEI);
db.execSQL(DATABASE_CUSER);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//Drop User Table if exist
db.execSQL(DROP_USER_TABLE);
// Create tables again
onCreate(db);
}
/**
* This method is to create user record
*
* #param user
*/
public void addUser(User user) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(userid,user.getUserid());
values.put(username, user.getName());
values.put(password, user.getPassword());
values.put(userrole, user.getRole());
// Inserting Row
db.insert(table_login, null, values);
db.close();
}
public void addIMEI(User user) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(imei,user.getImei());
values.put(imeiid, user.getImeiid());
// Inserting Row
db.insert(table_imei, null, values);
db.close();
}
/**
* This method is to fetch all user and return the list of user records
*
* #return list
*/
public List<User> getAllUser() {
// array of columns to fetch
String[] columns = {
userid,
username,
password,
userrole
};
// sorting orders
String sortOrder =
userid + " ASC";
List<User> userList = new ArrayList<User>();
SQLiteDatabase db = this.getReadableDatabase();
// query the user table
/**
* Here query function is used to fetch records from user table this function works like we use sql query.
* SQL query equivalent to this query function is
* SELECT user_id,user_name,user_email,user_password FROM user ORDER BY user_name;
*/
Cursor cursor = db.query(table_login, //Table to query
columns, //columns to return
null, //columns for the WHERE clause
null, //The values for the WHERE clause
null, //group the rows
null, //filter by row groups
sortOrder); //The sort order
// Traversing through all rows and adding to list
if (cursor.moveToFirst()) {
do {
User user = new User();
//user.setId(Integer.parseInt(cursor.getString(cursor.getColumnIndex(userid))));
user.setUserid(cursor.getString(cursor.getColumnIndex(userid)));
user.setName(cursor.getString(cursor.getColumnIndex(username)));
user.setPassword(cursor.getString(cursor.getColumnIndex(password)));
user.setRole(cursor.getString(cursor.getColumnIndex(userrole)));
// Adding user record to list
userList.add(user);
} while (cursor.moveToNext());
}
cursor.close();
db.close();
// return user list
return userList;
}
/**
* This method is to fetch all user and return the list of user records
*
* #return list
*/
public List<User> getAllImei() {
// array of columns to fetch
String[] columns = {
imeiid,
imei,
};
// sorting orders
String sortOrder =
imeiid + " ASC";
List<User> userList = new ArrayList<User>();
SQLiteDatabase db = this.getReadableDatabase();
// query the user table
/**
* Here query function is used to fetch records from user table this function works like we use sql query.
* SQL query equivalent to this query function is
* SELECT user_id,user_name,user_email,user_password FROM user ORDER BY user_name;
*/
Cursor cursor = db.query(table_imei, //Table to query
columns, //columns to return
null, //columns for the WHERE clause
null, //The values for the WHERE clause
null, //group the rows
null, //filter by row groups
sortOrder); //The sort order
// Traversing through all rows and adding to list
if (cursor.moveToFirst()) {
do {
User user = new User();
//user.setId(Integer.parseInt(cursor.getString(cursor.getColumnIndex(userid))));
user.setImei(cursor.getString(cursor.getColumnIndex(imei)));
user.setImeiid(cursor.getString(cursor.getColumnIndex(imeiid)));
// Adding user record to list
userList.add(user);
} while (cursor.moveToNext());
}
cursor.close();
db.close();
// return user list
return userList;
}
/**
* This method to update user record
*
* #param receivedUSERId
* #param updateUserRec
* #param user
*/
public void updateUser(String receivedUSERId, UpdateUserRec updateUserRec, User user) {
SQLiteDatabase db = this.getWritableDatabase();
String strSQL = "UPDATE "+table_login+ " SET "+username+" = "+user.getName()+"," +
" "+password+" = "+user.getPassword()+","+userrole+" = "+user.getRole()+"" +
" WHERE "+userid+" = "+receivedUSERId;
db.execSQL(strSQL);
db.close();
}
/**
* This method is to delete user record
*
* #param user
*/
public void deleteUser(User user) {
SQLiteDatabase db = this.getWritableDatabase();
// delete user record by id
db.delete(table_login, userid + " = ?",
new String[]{String.valueOf(user.getUserid())});
db.close();
}
/**
* This method to check user exist or not
*
#param userid
* #return true/false
*/
public boolean checkUser(String userid) {
// array of columns to fetch
String[] columns = {
userid
};
SQLiteDatabase db = this.getReadableDatabase();
// selection criteria
String selection = userid + " = ?";
// selection argument
String[] selectionArgs = {userid};
// query user table with condition
/**
* Here query function is used to fetch records from user table this function works like we use sql query.
* SQL query equivalent to this query function is
* SELECT user_id FROM user WHERE imei = 'dynamic#imei.com';
*/
Cursor cursor = db.query(table_login, //Table to query
columns, //columns to return
selection, //columns for the WHERE clause
selectionArgs, //The values for the WHERE clause
null, //group the rows
null, //filter by row groups
null); //The sort order
int cursorCount = cursor.getCount();
cursor.close();
db.close();
if (cursorCount > 0) {
return true;
}
return false;
}
/**
* This method to check user exist or not
*
* #param email
* #param password
* #return true/false
*/
public boolean checkUser(String email, String password) {
// array of columns to fetch
String[] columns = {
userid
};
SQLiteDatabase db = this.getReadableDatabase();
// selection criteria
String selection = userid + " = ?" + " AND " + password + " = ?";
// selection arguments
String[] selectionArgs = {email, password};
// query user table with conditions
/**
* Here query function is used to fetch records from user table this function works like we use sql query.
* SQL query equivalent to this query function is
* SELECT user_id FROM user WHERE user_email = 'jack#androidtutorialshub.com' AND user_password = 'qwerty';
*/
Cursor cursor = db.query(table_login, //Table to query
columns, //columns to return
selection, //columns for the WHERE clause
selectionArgs, //The values for the WHERE clause
null, //group the rows
null, //filter by row groups
null); //The sort order
int cursorCount = cursor.getCount();
cursor.close();
db.close();
if (cursorCount > 0) {
return true;
}
return false;
}
/**
* This method to check user exist or not
*
* #param userid
* #param username
* #return true/false
*/
public boolean checkUserData(String userid, String username) {
// array of columns to fetch
String[] columns = {
userid
};
SQLiteDatabase db = this.getReadableDatabase();
// selection criteria
String selection = userid + " = ?" + " AND " + username + " = ?";
// selection arguments
String[] selectionArgs = {userid, username};
// query user table with conditions
/**
* Here query function is used to fetch records from user table this function works like we use sql query.
* SQL query equivalent to this query function is
* SELECT user_id FROM user WHERE user_email = 'jack#androidtutorialshub.com' AND user_password = 'qwerty';
*/
Cursor cursor = db.query(table_login, //Table to query
columns, //columns to return
selection, //columns for the WHERE clause
selectionArgs, //The values for the WHERE clause
null, //group the rows
null, //filter by row groups
null); //The sort order
int cursorCount = cursor.getCount();
cursor.close();
db.close();
if (cursorCount > 0) {
return true;
}
return false;
}
public boolean checkUserData(String userid) {
// array of columns to fetch
String[] columns = {
userid
};
SQLiteDatabase db = this.getReadableDatabase();
// selection criteria
String selection = userid + " = ?";
// selection argument
String[] selectionArgs = {userid};
// query user table with condition
/**
* Here query function is used to fetch records from user table this function works like we use sql query.
* SQL query equivalent to this query function is
* SELECT user_id FROM user WHERE user_email = 'dynamic#data.com';
*/
Cursor cursor = db.query(table_login, //Table to query
columns, //columns to return
selection, //columns for the WHERE clause
selectionArgs, //The values for the WHERE clause
null, //group the rows
null, //filter by row groups
null); //The sort order
int cursorCount = cursor.getCount();
cursor.close();
db.close();
if (cursorCount > 0) {
return true;
}
return false;
}
public User getUser(String id){
SQLiteDatabase db = this.getWritableDatabase();
String query= "SELECT * FROM "+table_login;
Cursor cursor = db.rawQuery(query, null);
User user = new User();
if(cursor.getCount() > 0) {
cursor.moveToFirst();
user.setUserid(cursor.getString(cursor.getColumnIndex(userid)));
user.setName(cursor.getString(cursor.getColumnIndex(username)));
user.setPassword(cursor.getString(cursor.getColumnIndex(password)));
user.setRole(cursor.getString(cursor.getColumnIndex(userrole)));
}
return user;
}
public void deletePersonRecord(String useridValue, Context mContext) {
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL("DELETE FROM "+table_login+" WHERE "+userid +"='"+useridValue+"'");
Toast.makeText(mContext, "Deleted successfully.", Toast.LENGTH_SHORT).show();
db.close();
}
public void deleteIMEIRecord(String imeiidValue, Context mContext) {
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL("DELETE FROM "+table_imei+" WHERE "+imeiid +"='"+imeiidValue+"'");
Toast.makeText(mContext, "Deleted successfully.", Toast.LENGTH_SHORT).show();
}
public void updateIMEI(String receivedIMEIId, UpdateIMEIRec updateIMEIRec, User user) {
SQLiteDatabase db = this.getWritableDatabase();
String strSQL = "UPDATE "+table_imei+ " SET "+imei+" = "+user.getImei()+"," +
" "+imeiid+" = "+user.getImeiid()+
" WHERE "+imeiid+" = "+receivedIMEIId;
db.execSQL(strSQL);
db.close();
}
public User getIMEI(String receivedIMEIId) {
SQLiteDatabase db = this.getWritableDatabase();
String query = "SELECT * FROM " + table_imei ;
Cursor cursor = db.rawQuery(query, null);
User user = new User();
if(cursor.getCount() > 0) {
cursor.moveToFirst();
user.setImeiid(cursor.getString(cursor.getColumnIndex(imeiid)));
user.setImei(cursor.getString(cursor.getColumnIndex(imei)));
}
return user;
}
}
And Here is my Recycler List Activity Class
public class UserUpdateListActivity extends AppCompatActivity {
AppCompatActivity activity = UserUpdateListActivity.this;
AppCompatTextView textViewName;
RecyclerView recyclerViewUsers;
AppCompatButton textViewButtonNewUser;
List<User> listUsers;
UserRecyclerAdapterSavedUsers userRecyclerAdapterSavedUsers;
DatabaseHelper databaseHelper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_record_updated_list);
//getSupportActionBar().setTitle("");
initViews();
initObjects();
}
#Override
public void onBackPressed() {
super.onBackPressed();
startActivity(new Intent(UserUpdateListActivity.this,AdminMain.class));
finish();
}
#Override
protected void onRestart() {
super.onRestart();
}
/**
* This method is to initialize views
*/
private void initViews() {
textViewName = (AppCompatTextView) findViewById(R.id.textViewName);
textViewButtonNewUser = (AppCompatButton) findViewById(R.id.btnaddnew);
recyclerViewUsers = (RecyclerView) findViewById(R.id.recyclerViewUsers);
textViewButtonNewUser.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(UserUpdateListActivity.this,UserRecordSaveActivity.class));
}
});
}
/**
* This method is to initialize objects to be used
*/
private void initObjects() {
listUsers = new ArrayList<>();
userRecyclerAdapterSavedUsers = new UserRecyclerAdapterSavedUsers(listUsers,recyclerViewUsers);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recyclerViewUsers.setLayoutManager(mLayoutManager);
recyclerViewUsers.setItemAnimator(new DefaultItemAnimator());
recyclerViewUsers.setHasFixedSize(true);
recyclerViewUsers.setAdapter(userRecyclerAdapterSavedUsers);
databaseHelper = new DatabaseHelper(activity);
String emailFromIntent = getIntent().getStringExtra("USERS");
textViewName.setText(emailFromIntent);
getDataFromSQLite();
}
/**
* This method is to fetch all user records from SQLite
*/
private void getDataFromSQLite() {
// AsyncTask is used that SQLite operation not blocks the UI Thread.
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
listUsers.clear();
listUsers.addAll(databaseHelper.getAllUser());
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
userRecyclerAdapterSavedUsers.notifyDataSetChanged();
}
}.execute();
}
}
This is the Query I am using to delete the data
public void deletePersonRecord(String useridValue, Context mContext) {
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL("DELETE FROM "+table_login+" WHERE "+userid +"='"+useridValue+"'");
Toast.makeText(mContext, "Deleted successfully.", Toast.LENGTH_SHORT).show();
db.close();
}
Here is image of my recycler list
List Items
As I click on an Item It open me a Dialog like this
Dialog
When I click on delete this shows me this
Clicking on Delete
After that As I pressed back and again open the Data I deleted again open
Logcat has no Errors
Here is my update activity
public class UpdateUserRec extends AppCompatActivity {
EditText UserIDUpdate,UserNameUpdate,UserPasswordUpdate,UserRoleUpdate;
Button BtnUserRecUpdate;
DatabaseHelper dbHelper;
String receivedUSERId;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_update_record);
UserIDUpdate= (EditText) findViewById(R.id.useridupdate);
UserNameUpdate= (EditText) findViewById(R.id.usernameupdate);
UserPasswordUpdate= (EditText) findViewById(R.id.userpasswordupdate);
UserRoleUpdate= (EditText) findViewById(R.id.userroleupdate);
BtnUserRecUpdate= (Button) findViewById(R.id.userbtnupdate);
dbHelper = new DatabaseHelper(this);
try {
//get intent to get person id
receivedUSERId= getIntent().getStringExtra("USER_ID");
} catch (Exception e) {
e.printStackTrace();
}
User user= dbHelper.getUser(receivedUSERId);
UserIDUpdate.setText(user.getUserid());
UserNameUpdate.setText(user.getName());
UserPasswordUpdate.setText(user.getPassword());
UserRoleUpdate.setText(user.getRole());
BtnUserRecUpdate.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
updateUserFunction();
}
});
}
private void updateUserFunction() {
String useridupdate = UserIDUpdate.getText().toString().trim();
String usernameupdate = UserNameUpdate.getText().toString().trim();
String userpasswordupdate = UserRoleUpdate.getText().toString().trim();
String userroleupdate = UserRoleUpdate.getText().toString().trim();
if(useridupdate.isEmpty()){
//error name is empty
Toast.makeText(this, "Enter User ID", Toast.LENGTH_LONG).show();
}
if(usernameupdate.isEmpty()){
//error name is empty
Toast.makeText(this, "Enter User Name", Toast.LENGTH_LONG).show();
}
if(userpasswordupdate.isEmpty()){
//error name is empty
Toast.makeText(this, "Enter the password", Toast.LENGTH_LONG).show();
}
if(userroleupdate.isEmpty()){
//error name is empty
Toast.makeText(this, "Enter User Role", Toast.LENGTH_LONG).show();
}
//create updated person
User user = new User();
//call dbhelper update
dbHelper.updateUser(receivedUSERId, this, user);
//finally redirect back home
// NOTE you can implement an sqlite callback then redirect on success delete
goBackHome();
}
private void goBackHome() {
startActivity(new Intent(UpdateUserRec.this,UsersListActivity.class));
}
}
You are deleting data from the list that is showing data in RecyclerView. You should keep in mind Database is different entity from your list. Data base is persistent storage for data and list from where you deleting item is non persistent storage. Next time when you get data from Database the list will again fill with items that you previously deleted.
Correct method is
Delete the row from Database
Remove item from list in adapter
notifyItemRemoved(position)
You have this method in SQL class
public List<User> getAllUser() { ... }
Create one more method
public boolean deleteUser(id){
db.delete(DATABASE_TABLE, KEY_NAME + "=" + id, null)
}
On delete item call this method from your adapter with specific Id then do the same as you doing previously.
try changing your delete functionality with this and see if it works
builder.setNeutralButton("Delete", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//go to Remove Item
DatabaseHelper dbHelper = new DatabaseHelper(mContext);
dbHelper.deletePersonRecord(listUsers.get(position).getUserid(), mContext);
listUsers.remove( position);
notifyItemRemoved(position);
mRecyclerView.removeViewAt(position);
notifyItemRangeChanged(position, listUsers.size());
notifyDataSetChanged();
dialog.cancel();
}
});
EDIT: for your new problem: about updating user
change your setPositiveButton functionality like this
builder.setPositiveButton("Update", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//go to update activity
gotupdateuserActivity(listUsers.get(position).getUserid());
// dialog.cancel();
}
});

App crashes because of null Cursor - Content Providers

I am following the data storage lessons from udacity. And in their app, you insert an animal's details such as name, breed, gender and weight. In the CatalogActivity I insert some dummy data,and read them from the provider through the ContentResolver. And in the EditorActivity I insert the data manually.
Basically the query method returns a Cursor object that holds the rows of our table. The ContentResolver passes the query method to the PetProvider. Then the PetProvider performs two operations for now. Query and Insert. This is the code of my provider.
PetProvider
/**
* {#link ContentProvider} for Pets app.
*/
public class PetProvider extends ContentProvider {
private PetHelper mHelper;
/** Tag for the log messages */
public static final String LOG_TAG = PetProvider.class.getSimpleName();
/**
* URI matcher code for the content URI for the pets table
*/
public static final int PETS = 100;
/**
* URI matcher code for the content URI for a single pet in the pets table
*/
public static final int PET_ID = 101;
/** URI matcher object to match a context URI to a corresponding code.
* The input passed into the constructor represents the code to return for the root URI.
* It's common to use NO_MATCH as the input for this case.
*/
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// Static initializer. This is run the first time anything is called from this class.
static{
// The calls to addURI() go here, for all of the content URI patterns that the provider
// should recognize. All paths added to the UriMatcher have a corresponding code to return
// when a match is found.
// The content URI of the form "content://com.example.android.pets/pets" will map to the
// integer code {#link #PETS}. This URI is used to provide access to MULTIPLE rows
// of the pets table.
sUriMatcher.addURI(PetContract.CONTENT_AUTHORITY,PetContract.PATH_PETS,PETS);
// The content URI of the form "content://com.example.android.pets/pets/#" will map to the
// integer code {#link #PETS_ID}. This URI is used to provide access to ONE single row
// of the pets table.
// In this case, the "#" wildcard is used where "#" can be substituted for an integer.
// For example, "content://com.example.android.pets/pets/3" matches, but
// "content://com.example.android.pets/pets" (without a number at the end) doesn't match.
sUriMatcher.addURI(PetContract.CONTENT_AUTHORITY, PetContract.PATH_PETS + "/#", PET_ID);
}
/**
* Initialize the provider and the database helper object.
*/
#Override
public boolean onCreate() {
mHelper = new PetHelper(getContext());
return true;
}
/**
* Perform the query for the given URI. Use the given projection, selection, selection arguments, and sort order.
*/
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// Get readable database
SQLiteDatabase database = mHelper.getReadableDatabase();
// This cursor will hold the result of the query
Cursor cursor;
// Figure out if the URI matcher can match the URI to a specific code
int match = sUriMatcher.match(uri);
switch (match) {
case PETS:
// For the PETS code, query the pets table directly with the given
// projection, selection, selection arguments, and sort order. The cursor
// could contain multiple rows of the pets table.
cursor = database.query(PetContract.PetEntry.TABLE_NAME, projection, selection, selectionArgs,
null, null, sortOrder);
break;
case PET_ID:
// For the PET_ID code, extract out the ID from the URI.
// For an example URI such as "content://com.example.android.pets/pets/3",
// the selection will be "_id=?" and the selection argument will be a
// String array containing the actual ID of 3 in this case.
//
// For every "?" in the selection, we need to have an element in the selection
// arguments that will fill in the "?". Since we have 1 question mark in the
// selection, we have 1 String in the selection arguments' String array.
selection = PetContract.PetEntry._ID + "=?";
selectionArgs = new String[] { String.valueOf(ContentUris.parseId(uri)) };
// This will perform a query on the pets table where the _id equals 3 to return a
// Cursor containing that row of the table.
cursor = database.query(PetContract.PetEntry.TABLE_NAME, projection, selection, selectionArgs,
null, null, sortOrder);
break;
default:
throw new IllegalArgumentException("Cannot query unknown URI " + uri);
}
return cursor;
}
/**
* Insert new data into the provider with the given ContentValues.
*/
#Override
public Uri insert(Uri uri, ContentValues contentValues) {
final int match = sUriMatcher.match(uri);
switch (match) {
case PETS:
return insertPet(uri, contentValues);
default:
throw new IllegalArgumentException("Insertion is not supported for " + uri);
}
}
private Uri insertPet(Uri uri, ContentValues values) {
// Get writeable database
SQLiteDatabase database = mHelper.getWritableDatabase();
// Insert the new pet with the given values
long id = database.insert(PetContract.PetEntry.TABLE_NAME, null, values);
// If the ID is -1, then the insertion failed. Log an error and return null.
if (id == -1) {
Log.e(LOG_TAG, "Failed to insert row for " + uri);
return null;
}
// Return the new URI with the ID (of the newly inserted row) appended at the end
return ContentUris.withAppendedId(uri, id);
}
/**
* Updates the data at the given selection and selection arguments, with the new ContentValues.
*/
#Override
public int update(Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
return 0;
}
/**
* Delete the data at the given selection and selection arguments.
*/
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
/**
* Returns the MIME type of data for the content URI.
*/
#Override
public String getType(Uri uri) {
return null;
}
}
Then in CatalogueActivity, I use the query(...) method of the ContentResolver. So
CatalogActivity
public class CatalogActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_catalog);
// Setup FAB to open EditorActivity
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(CatalogActivity.this, EditorActivity.class);
startActivity(intent);
}
});
}
#Override
protected void onStart() {
super.onStart();
displayDatabaseInfo();
}
/**
* Temporary helper method to display information in the onscreen TextView about the state of
* the pets database.
*/
private void displayDatabaseInfo() {
// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
PetEntry._ID,
PetEntry.COLUMN_PET_NAME,
PetEntry.COLUMN_PET_BREED,
PetEntry.COLUMN_PET_GENDER,
PetEntry.COLUMN_PET_WEIGHT };
// Perform a query on the provider using the ContentResolver.
// Use the {#link PetEntry#CONTENT_URI} to access the pet data.
Cursor cursor = getContentResolver().query(
PetEntry.CONTENT_URI, // The content URI of the words table
projection, // The columns to return for each row
null, // Selection criteria
null, // Selection criteria
null); // The sort order for the returned rows
TextView displayView = (TextView) findViewById(R.id.text_view_pet);
try {
// Create a header in the Text View that looks like this:
//
// The pets table contains <number of rows in Cursor> pets.
// _id - name - breed - gender - weight
//
// In the while loop below, iterate through the rows of the cursor and display
// the information from each column in this order.
displayView.setText("The pets table contains " + cursor.getCount() + " pets.\n\n");
displayView.append(PetEntry._ID + " - " +
PetEntry.COLUMN_PET_NAME + " - " +
PetEntry.COLUMN_PET_BREED + " - " +
PetEntry.COLUMN_PET_GENDER + " - " +
PetEntry.COLUMN_PET_WEIGHT + "\n");
// Figure out the index of each column
int idColumnIndex = cursor.getColumnIndex(PetEntry._ID);
int nameColumnIndex = cursor.getColumnIndex(PetEntry.COLUMN_PET_NAME);
int breedColumnIndex = cursor.getColumnIndex(PetEntry.COLUMN_PET_BREED);
int genderColumnIndex = cursor.getColumnIndex(PetEntry.COLUMN_PET_GENDER);
int weightColumnIndex = cursor.getColumnIndex(PetEntry.COLUMN_PET_WEIGHT);
// Iterate through all the returned rows in the cursor
while (cursor.moveToNext()) {
// Use that index to extract the String or Int value of the word
// at the current row the cursor is on.
int currentID = cursor.getInt(idColumnIndex);
String currentName = cursor.getString(nameColumnIndex);
String currentBreed = cursor.getString(breedColumnIndex);
int currentGender = cursor.getInt(genderColumnIndex);
int currentWeight = cursor.getInt(weightColumnIndex);
// Display the values from each column of the current row in the cursor in the TextView
displayView.append(("\n" + currentID + " - " +
currentName + " - " +
currentBreed + " - " +
currentGender + " - " +
currentWeight));
}
} finally {
// Always close the cursor when you're done reading from it. This releases all its
// resources and makes it invalid.
cursor.close();
}
}
/**
* Helper method to insert hardcoded pet data into the database. For debugging purposes only.
*/
private void insertPet() {
// Create a ContentValues object where column names are the keys,
// and Toto's pet attributes are the values.
ContentValues values = new ContentValues();
values.put(PetEntry.COLUMN_PET_NAME, "Toto");
values.put(PetEntry.COLUMN_PET_BREED, "Terrier");
values.put(PetEntry.COLUMN_PET_GENDER, PetEntry.GENDER_MALE);
values.put(PetEntry.COLUMN_PET_WEIGHT, 7);
// Insert a new row for Toto into the provider using the ContentResolver.
// Use the {#link PetEntry#CONTENT_URI} to indicate that we want to insert
// into the pets database table.
// Receive the new content URI that will allow us to access Toto's data in the future.
Uri newUri = getContentResolver().insert(PetEntry.CONTENT_URI, values);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu options from the res/menu/menu_catalog.xml file.
// This adds menu items to the app bar.
getMenuInflater().inflate(R.menu.menu_catalog, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// User clicked on a menu option in the app bar overflow menu
switch (item.getItemId()) {
// Respond to a click on the "Insert dummy data" menu option
case R.id.action_insert_dummy_data:
insertPet();
displayDatabaseInfo();
return true;
// Respond to a click on the "Delete all entries" menu option
case R.id.action_delete_all_entries:
// Do nothing for now
return true;
}
return super.onOptionsItemSelected(item);
}
}
And the EditorActiviy
**
* Allows user to create a new pet or edit an existing one.
*/
public class EditorActivity extends AppCompatActivity {
/** EditText field to enter the pet's name */
private EditText mNameEditText;
/** EditText field to enter the pet's breed */
private EditText mBreedEditText;
/** EditText field to enter the pet's weight */
private EditText mWeightEditText;
/** EditText field to enter the pet's gender */
private Spinner mGenderSpinner;
/**
* Gender of the pet. The possible values are:
* 0 for unknown gender, 1 for male, 2 for female.
*/
private int mGender = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_editor);
// Find all relevant views that we will need to read user input from
mNameEditText = (EditText) findViewById(R.id.edit_pet_name);
mBreedEditText = (EditText) findViewById(R.id.edit_pet_breed);
mWeightEditText = (EditText) findViewById(R.id.edit_pet_weight);
mGenderSpinner = (Spinner) findViewById(R.id.spinner_gender);
setupSpinner();
}
/**
* Setup the dropdown spinner that allows the user to select the gender of the pet.
*/
private void setupSpinner() {
// Create adapter for spinner. The list options are from the String array it will use
// the spinner will use the default layout
ArrayAdapter genderSpinnerAdapter = ArrayAdapter.createFromResource(this,
R.array.array_gender_options, android.R.layout.simple_spinner_item);
// Specify dropdown layout style - simple list view with 1 item per line
genderSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_dropdown_item_1line);
// Apply the adapter to the spinner
mGenderSpinner.setAdapter(genderSpinnerAdapter);
// Set the integer mSelected to the constant values
mGenderSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String selection = (String) parent.getItemAtPosition(position);
if (!TextUtils.isEmpty(selection)) {
if (selection.equals(getString(R.string.gender_male))) {
mGender = 1; // Male
} else if (selection.equals(getString(R.string.gender_female))) {
mGender = 2; // Female
} else {
mGender = 0; // Unknown
}
}
}
// Because AdapterView is an abstract class, onNothingSelected must be defined
#Override
public void onNothingSelected(AdapterView<?> parent) {
mGender = 0; // Unknown
}
});
}
/**
* Get user input from editor and save new pet into database.
*/
private void insertPet() {
// Read from input fields
// Use trim to eliminate leading or trailing white space
String nameString = mNameEditText.getText().toString().trim();
String breedString = mBreedEditText.getText().toString().trim();
String weightString = mWeightEditText.getText().toString().trim();
int weight = Integer.parseInt(weightString);
// Create a ContentValues object where column names are the keys,
// and pet attributes from the editor are the values.
ContentValues values = new ContentValues();
values.put(PetEntry.COLUMN_PET_NAME, nameString);
values.put(PetEntry.COLUMN_PET_BREED, breedString);
values.put(PetEntry.COLUMN_PET_GENDER, mGender);
values.put(PetEntry.COLUMN_PET_WEIGHT, weight);
// Insert a new pet into the provider, returning the content URI for the new pet.
Uri newUri = getContentResolver().insert(PetEntry.CONTENT_URI, values);
// Show a toast message depending on whether or not the insertion was successful
if (newUri == null) {
// If the new content URI is null, then there was an error with insertion.
Toast.makeText(this, getString(R.string.editor_insert_pet_failed),
Toast.LENGTH_SHORT).show();
} else {
// Otherwise, the insertion was successful and we can display a toast.
Toast.makeText(this, getString(R.string.editor_insert_pet_successful),
Toast.LENGTH_SHORT).show();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu options from the res/menu/menu_editor.xml file.
// This adds menu items to the app bar.
getMenuInflater().inflate(R.menu.menu_editor, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// User clicked on a menu option in the app bar overflow menu
switch (item.getItemId()) {
// Respond to a click on the "Save" menu option
case R.id.action_save:
insertPet();
return true;
// Respond to a click on the "Delete" menu option
case R.id.action_delete:
// Do nothing for now
return true;
// Respond to a click on the "Up" arrow button in the app bar
case android.R.id.home:
// Navigate back to parent activity (CatalogActivity)
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
}
The exception occurs in CatalogActivity.
Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'void android.database.Cursor.close()' on a null object reference
PetContract
public final class PetContract {
// To prevent someone from accidentally instantiating the contract class,
// give it an empty constructor.
private PetContract() {}
/**
* The "Content authority" is a name for the entire content provider, similar to the
* relationship between a domain name and its website. A convenient string to use for the
* content authority is the package name for the app, which is guaranteed to be unique on the
* device.
*/
public static final String CONTENT_AUTHORITY = "com.example.android.pets";
/**
* Use CONTENT_AUTHORITY to create the base of all URI's which apps will use to contact
* the content provider.
*/
public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
/**
* Possible path (appended to base content URI for possible URI's)
* For instance, content://com.example.android.pets/pets/ is a valid path for
* looking at pet data. content://com.example.android.pets/staff/ will fail,
* as the ContentProvider hasn't been given any information on what to do with "staff".
*/
public static final String PATH_PETS = "pets";
/**
* Inner class that defines constant values for the pets database table.
* Each entry in the table represents a single pet.
*/
public static final class PetEntry implements BaseColumns {
/** The content URI to access the pet data in the provider */
public static final Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, PATH_PETS);
/** Name of database table for pets */
public final static String TABLE_NAME = "pets";
/**
* Unique ID number for the pet (only for use in the database table).
*
* Type: INTEGER
*/
public final static String _ID = BaseColumns._ID;
/**
* Name of the pet.
*
* Type: TEXT
*/
public final static String COLUMN_PET_NAME ="name";
/**
* Breed of the pet.
*
* Type: TEXT
*/
public final static String COLUMN_PET_BREED = "breed";
/**
* Gender of the pet.
*
* The only possible values are {#link #GENDER_UNKNOWN}, {#link #GENDER_MALE},
* or {#link #GENDER_FEMALE}.
*
* Type: INTEGER
*/
public final static String COLUMN_PET_GENDER = "gender";
/**
* Weight of the pet.
*
* Type: INTEGER
*/
public final static String COLUMN_PET_WEIGHT = "weight";
/**
* Possible values for the gender of the pet.
*/
public static final int GENDER_UNKNOWN = 0;
public static final int GENDER_MALE = 1;
public static final int GENDER_FEMALE = 2;
}
}
Any ideas?
Thanks,
Theo.
Your content URI is not matching while query, so it's throwing IllegalArgumentException, and your cursor is null, but you are trying to close the cursor, so it's crashing
In final check null before close,
finally {
// Always close the cursor when you're done reading from it. This releases all its
// resources and makes it invalid.
if(cursor != null)
cursor.close();
}
Check your content URI while querying in CatalogActivity,
Update your getType() too its returning null
I just did this and my app worked:
In PetContract.PetEntry add
public static final Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, PATH_PETS);
then in catalogue activity and edit activity use CONTENT_URI instead of BASE_CONTENT_URI.
<provider android:name="com.example.android.pets.PetProvider"
android:authorities="com.example.android.pets">
</provider>
Just use this provider tag in manifest file. I have used it. and it is working fine
Use this in your suriMatcher:
sUriMatcher.addURI(PetContract.CONTENT_AUTHORITY, PetContract.PATH_PETS + "/#", PET_ID);
instead of this:
sURIMatcher.addURI(petcontract.CONTENT_AUTHORITY, pet_Path,PETS_ID);

Deleting data from SQLite database doesn't work in Fragment

I'm having an Activity which handles a details Fragment for my Movie data & I want to implement favorite functionality. But the issue is that I can favorite only one movie at a time. Also, each time I try to add/favorite a movie, it'll show on the database that it's being saved but never being deleted on unfavorite.
Here's the code that I've:
MovieDetailsActivity
public class MovieDetailsActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/* Set the content of the activity to use the activity_tv_show_details.xml layout file */
setContentView(R.layout.activity_movie_details);
Bundle movieDetails = new Bundle();
/**get the movie's Object from the parent activity**/
Movie movie = getIntent().getParcelableExtra("movie");
movieDetails.putParcelable("movie", movie);
Intent intent = getIntent();
Uri mCurrentMovieUri = intent.getData();
movieDetails.putString("currentMovieUri", mCurrentMovieUri.toString());
/* Check for pre-existing instances of fragments(here explicitly check for savedInstance)
and then begin fragment transaction accordingly */
if (savedInstanceState == null) {
MovieDetailsFragment defaultMovieFragment = new MovieDetailsFragment();
defaultMovieFragment.setArguments(movieDetails);
getSupportFragmentManager().beginTransaction()
.add(R.id.containerMovieDetailActivity, defaultMovieFragment)
.commit();
}
}
}
MovieDetailsFragment
public class MovieDetailsFragment extends Fragment implements LoaderManager.LoaderCallbacks<MovieDetailsBundle> {
private static final int MOVIE_DETAIL_LOADER_ID = 2;
/* Arrays for holding movie details */
Movie movie;
public MovieDetailsFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_movie_detail, container, false);
Bundle bundle = getArguments();
position = bundle.getInt("position");
currentMovieUri = Uri.parse(bundle.getString("currentMovieUri"));
favoriteButton = (ImageButton) rootView.findViewById(R.id.favorite);
if (savedInstanceState == null) {
mReview = new ArrayList<>();
mVideo = new ArrayList<>();
mCredits = new ArrayList<>();
mMovieDetailsBundle = new MovieDetailsBundle();
}
if ((bundle != null)) {
movie = getArguments().getParcelable("movie");
movieDetailTitleTextView.setText(movie.getMovieTitle());
...
String[] projection = {
MoviesEntry._ID,
MoviesEntry.COLUMN_MOVIE_TITLE,
MoviesEntry.COLUMN_MOVIE_RELEASE_DATE,
MoviesEntry.COLUMN_MOVIE_OVERVIEW,
MoviesEntry.COLUMN_MOVIE_POSTER_URL,
MoviesEntry.COLUMN_MOVIE_BACKDROP_URL,
MoviesEntry.COLUMN_MOVIE_RATING};
// Perform a query on the provider using the ContentResolver.
// Use the {#link MoviesEntry#CONTENT_URI} to access the pet data.
Cursor cursor = getActivity().getContentResolver().query(
MoviesEntry.CONTENT_URI, // The content URI of the movies table
projection, // The columns to return for each row
null, // Selection criteria
null, // Selection criteria
null);
try {
// Figure out the index of each column
int idColumnIndex = cursor.getColumnIndex(MoviesEntry._ID);
int titleColumnIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIE_TITLE);
int releaseDateColumnIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIE_RELEASE_DATE);
int overviewColumnIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIE_OVERVIEW);
int posterUrlColumnIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIE_POSTER_URL);
int backdropUrlColumnIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIE_BACKDROP_URL);
int ratingColumnIndex = cursor.getColumnIndex(MoviesEntry.COLUMN_MOVIE_RATING);
// Iterate through all the returned rows in the cursor
if (cursor.moveToFirst()){
// Use that index to extract the String or Int value of the word
// at the current row the cursor is on.
currentID = cursor.getInt(idColumnIndex);
currentTitle = cursor.getString(titleColumnIndex);
currentReleaseDate = cursor.getString(releaseDateColumnIndex);
currentOverview = cursor.getString(overviewColumnIndex);
currentposterUrl = cursor.getString(posterUrlColumnIndex);
currentBackdropUrl = cursor.getString(backdropUrlColumnIndex);
currentRatings = cursor.getFloat(ratingColumnIndex);
}
} finally {
// Always close the cursor when you're done reading from it. This releases all its
// resources and makes it invalid.
cursor.close();
}
if (currentTitle!=null) {
//currentTitle = movie.getMovieTitle();
if (currentTitle.equals(movie.getMovieTitle())) {
favoriteButton.setImageResource(R.drawable.starred);
favorite = true;
}
}else{
favoriteButton.setImageResource(R.drawable.unstarred);
favorite = false;
}
/*setting the ratingbar from #link: https://github.com/FlyingPumba/SimpleRatingBar*/
SimpleRatingBar simpleRatingBar = (SimpleRatingBar) rootView.findViewById(R.id.movieRatingInsideMovieDetailsFragment);
simpleRatingBar.setRating((float) (movie.getMovieVoteAverage()) / 2);
favoriteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (favorite) {
// Only perform the delete if this is an existing movie.
if (currentMovieUri != null) {
// Call the ContentResolver to delete the movie at the given content URI.
// Pass in null for the selection and selection args because the mCurrentPetUri
// content URI already identifies the movie that we want.
int rowsDeleted = getActivity().getContentResolver().delete(currentMovieUri, null, null);
// Show a toast message depending on whether or not the delete was successful.
if (rowsDeleted == 0) {
// If no rows were deleted, then there was an error with the delete.
Toast.makeText(getContext(), getString(R.string.delete_movie_failed),
Toast.LENGTH_SHORT).show();
} else {
// Otherwise, the delete was successful and we can display a toast.
Toast.makeText(getContext(), getString(R.string.delete_movie_successful),
Toast.LENGTH_SHORT).show();
}
}
favoriteButton.setImageResource(R.drawable.unstarred);
} else {
// Create a ContentValues object where column names are the keys,
// and movie attributes from the editor are the values.
ContentValues values = new ContentValues();
values.put(MoviesEntry.COLUMN_MOVIE_TITLE, movie.getMovieTitle());
values.put(MoviesEntry.COLUMN_MOVIE_RELEASE_DATE, movie.getMovieReleaseDate());
values.put(MoviesEntry.COLUMN_MOVIE_OVERVIEW, movie.getMovieOverview());
values.put(MoviesEntry.COLUMN_MOVIE_POSTER_URL, movie.getMoviePosterPath());
values.put(MoviesEntry.COLUMN_MOVIE_BACKDROP_URL, movie.getMovieBackdropPath());
values.put(MoviesEntry.COLUMN_MOVIE_RATING, movie.getMovieVoteAverage() / 2);
// Insert a new movie into the provider, returning the content URI for the new movie.
Uri newUri = getActivity().getContentResolver().insert(MoviesEntry.CONTENT_URI, values);
// Show a toast message depending on whether or not the insertion was successful
if (newUri == null) {
// If the new content URI is null, then there was an error with insertion.
Toast.makeText(getContext(), getString(R.string.insert_movie_failed),
Toast.LENGTH_SHORT).show();
} else {
// Otherwise, the insertion was successful and we can display a toast.
Toast.makeText(getContext(), getString(R.string.insert_movie_successful),
Toast.LENGTH_SHORT).show();
}
favoriteButton.setImageResource(R.drawable.starred);
}
favorite = !favorite;
}
});
...
#Override
public Loader<MovieDetailsBundle> onCreateLoader(int id, Bundle args) {
Uri baseUri = Uri.parse((UrlsAndConstants.MovieDetailQuery.DEFAULT_URL) + movie.getMovieId());
Uri.Builder uriBuilder = baseUri.buildUpon();
uriBuilder.appendQueryParameter(API_KEY_PARAM, API_KEY_PARAM_VALUE);
uriBuilder.appendQueryParameter(APPEND_TO_RESPONSE, VIDEOS_AND_REVIEWS_AND_CREDITS);
return new DetailsMovieLoader(getActivity().getApplicationContext(), uriBuilder.toString());
}
#Override
public void onLoadFinished(Loader<MovieDetailsBundle> loader, )
...
}
public void updateDurationTextView(MovieDetailsBundle movieDetailsBundle) {
...
movieRunTimeDuration.setText(mMovieDurationString);
}
#Override
public void onLoaderReset(Loader<MovieDetailsBundle> loader) {
}
}
ContentProvider
public class MovieProvider extends ContentProvider {
/**
* URI matcher code for the content URI for the movies table
*/
private static final int MOVIES = 100;
/**
* URI matcher code for the content URI for a single movie in the movie table
*/
private static final int MOVIE_ID = 200;
/**
* UriMatcher object to match a content URI to a corresponding code.
* The input passed into the constructor represents the code to return for the root URI.
* It's common to use NO_MATCH as the input for this case.
*/
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// Static initializer. This is run the first time anything is called from this class.
static {
// The calls to addURI() go here, for all of the content URI patterns that the provider
// should recognize. All paths added to the UriMatcher have a corresponding code to return
// when a match is found.
// The content URI of the form "content://com.example.android.movie/movie" will map to the
// integer code {#link #MOVIES}. This URI is used to provide access to MULTIPLE rows
// of the movie table.
sUriMatcher.addURI(MovieContract.CONTENT_AUTHORITY, MovieContract.PATH_MOVIES, MOVIES);
// The content URI of the form "content://com.example.android.movie/movie/#" will map to the
// integer code {#link #MOVIES}. This URI is used to provide access to ONE single row
// of the movie table.
//
// In this case, the "#" wildcard is used where "#" can be substituted for an integer.
// For example, "content://com.example.android.movie/movie/3" matches, but
// "content://com.example.android.movie/movie" (without a number at the end) doesn't match.
sUriMatcher.addURI(MovieContract.CONTENT_AUTHORITY, MovieContract.PATH_MOVIES + "/#", MOVIE_ID);
}
/**
* Database helper object
*/
private MovieDbHelper mDbHelper;
#Override
public boolean onCreate() {
mDbHelper = new MovieDbHelper(getContext());
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// Get readable database
SQLiteDatabase database = mDbHelper.getReadableDatabase();
// This cursor will hold the result of the query
Cursor cursor;
// Figure out if the URI matcher can match the URI to a specific code
int match = sUriMatcher.match(uri);
switch (match) {
case MOVIES:
// For the MOVIES code, query the movie table directly with the given
// projection, selection, selection arguments, and sort order. The cursor
// could contain multiple rows of the movie table.
cursor = database.query(MoviesEntry.TABLE_NAME, projection, selection, selectionArgs,
null, null, sortOrder);
break;
case MOVIE_ID:
// For the MOVIE_ID code, extract out the ID from the URI.
// For an example URI such as "content://com.example.android.movie/movie/3",
// the selection will be "_id=?" and the selection argument will be a
// String array containing the actual ID of 3 in this case.
//
// For every "?" in the selection, we need to have an element in the selection
// arguments that will fill in the "?". Since we have 1 question mark in the
// selection, we have 1 String in the selection arguments' String array.
selection = MoviesEntry._ID + "=?";
selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
// This will perform a query on the movie table where the _id equals 3 to return a
// Cursor containing that row of the table.
cursor = database.query(MoviesEntry.TABLE_NAME, projection, selection, selectionArgs,
null, null, sortOrder);
break;
default:
throw new IllegalArgumentException("Cannot query unknown URI " + uri);
}
// Set notification URI on the Cursor,
// so we know what content URI the Cursor was created for.
// If the data at this URI changes, then we know we need to update the Cursor.
cursor.setNotificationUri(getContext().getContentResolver(), uri);
// Return the cursor
return cursor;
}
#Override
public Uri insert(Uri uri, ContentValues contentValues) {
final int match = sUriMatcher.match(uri);
switch (match) {
case MOVIES:
return insertMovie(uri, contentValues);
default:
throw new IllegalArgumentException("Insertion is not supported for " + uri);
}
}
/**
* Insert a movie into the database with the given content values. Return the new content URI
* for that specific row in the database.
*/
private Uri insertMovie(Uri uri, ContentValues values) {
// Log.v("my_tag", "Received Uri to be matched insert is :"+uri.toString());
Log.i("my_tag", "Received Uri to be matched insert is :"+uri.toString());
// Check that the product name is not null
String title = values.getAsString(MoviesEntry.COLUMN_MOVIE_TITLE);
if (title == null) {
throw new IllegalArgumentException("Product requires a name");
}
// Check that the product name is not null
String releaseDate = values.getAsString(MoviesEntry.COLUMN_MOVIE_RELEASE_DATE);
if (releaseDate == null) {
throw new IllegalArgumentException("Product requires a detail");
}
// If the price is provided, check that it's greater than or equal to 0
String overview = values.getAsString(MoviesEntry.COLUMN_MOVIE_OVERVIEW);
if (overview == null) {
throw new IllegalArgumentException("Product requires valid price");
}
String posterUrl = values.getAsString(MoviesEntry.COLUMN_MOVIE_POSTER_URL);
if (posterUrl == null) {
throw new IllegalArgumentException("Product requires valid quantity");
}
String backdropUrl = values.getAsString(MoviesEntry.COLUMN_MOVIE_BACKDROP_URL);
if (backdropUrl == null) {
throw new IllegalArgumentException("Product requires valid quantity");
}
Integer ratings = values.getAsInteger(MoviesEntry.COLUMN_MOVIE_RATING);
if (ratings != null && ratings < 0) {
throw new IllegalArgumentException("Product requires valid quantity");
}
// Get writeable database
SQLiteDatabase database = mDbHelper.getWritableDatabase();
// Insert the new product with the given values
long id = database.insert(MoviesEntry.TABLE_NAME, null, values);
Log.i("my_tag", "values is :"+values.toString());
// Log.e("my_tag", "values is :"+values.toString());
// Log.v("my_tag", "values is :"+values.toString());
if (id == -1) {
return null;
}
// Notify all listeners that the data has changed for the product content URI
getContext().getContentResolver().notifyChange(uri, null);
// Return the new URI with the ID (of the newly inserted row) appended at the end
return ContentUris.withAppendedId(uri, id);
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Get writeable database
SQLiteDatabase database = mDbHelper.getWritableDatabase();
// Track the number of rows that were deleted
int rowsDeleted;
final int match = sUriMatcher.match(uri);
case MOVIES:
// Delete all rows that match the selection and selection args
rowsDeleted = database.delete(MoviesEntry.TABLE_NAME, selection, selectionArgs);
break;
case MOVIE_ID:
// Delete a single row given by the ID in the URI
selection = MoviesEntry._ID + "=?";
selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
Log.i("my_tag", "selection is :"+selection);
// Log.e("my_tag", "selection is :"+selection);
// Log.v("my_tag", "selection is :"+selection);
Log.i("my_tag", "selectionArgs is :"+selectionArgs[0]);
// Log.e("my_tag", "selectionArgs is :"+selectionArgs[0]);
// Log.v("my_tag", "selectionArgs is :"+selectionArgs[0]);
rowsDeleted = database.delete(MoviesEntry.TABLE_NAME, selection, selectionArgs);
break;
default:
throw new IllegalArgumentException("Deletion is not supported for " + uri);
}
// If 1 or more rows were deleted, then notify all listeners that the data at the
// given URI has changed
if (rowsDeleted != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
// Return the number of rows deleted
return rowsDeleted;
}
#Override
public int update(Uri uri, ContentValues contentValues, String selection,
String[] selectionArgs) {
...
}
#Override
public String getType(Uri uri) {
final int match = sUriMatcher.match(uri);
switch (match) {
case MOVIES:
return MoviesEntry.CONTENT_LIST_TYPE;
case MOVIE_ID:
return MoviesEntry.CONTENT_ITEM_TYPE;
default:
throw new IllegalStateException("Unknown URI " + uri + " with match " + match);
}
}
}

Trying to add a new column

guys, I'm trying to add a new column (KEY_EXAMPLE) in my "searchable dictionary" code example
The KEY_EXAMPLE should be the third column. But it doesn't work....
Here is my code:
DictionaryDatabase.Java
public class DictionaryDatabase {
private static final String TAG = "DictionaryDatabase";
//The columns we'll include in the dictionary table
public static final String KEY_WORD = SearchManager.SUGGEST_COLUMN_TEXT_1;
public static final String KEY_DEFINITION = SearchManager.SUGGEST_COLUMN_TEXT_2;
public static final String KEY_EXAMPLE = SearchManager.SUGGEST_COLUMN_TEXT_1;
private static final String DATABASE_NAME = "dictionary";
private static final String FTS_VIRTUAL_TABLE = "FTSdictionary";
private static final int DATABASE_VERSION = 2;
private final DictionaryOpenHelper mDatabaseOpenHelper;
private static final HashMap<String,String> mColumnMap = buildColumnMap();
/**
* Constructor
* #param context The Context within which to work, used to create the DB
*/
public DictionaryDatabase(Context context) {
mDatabaseOpenHelper = new DictionaryOpenHelper(context);
}
/**
* Builds a map for all columns that may be requested, which will be given to the
* SQLiteQueryBuilder. This is a good way to define aliases for column names, but must include
* all columns, even if the value is the key. This allows the ContentProvider to request
* columns w/o the need to know real column names and create the alias itself.
*/
private static HashMap<String,String> buildColumnMap() {
HashMap<String,String> map = new HashMap<String,String>();
map.put(KEY_WORD, KEY_WORD);
map.put(KEY_DEFINITION, KEY_DEFINITION);
map.put(KEY_EXAMPLE, KEY_EXAMPLE);
map.put(BaseColumns._ID, "rowid AS " +
BaseColumns._ID);
map.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, "rowid AS " +
SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
map.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, "rowid AS " +
SearchManager.SUGGEST_COLUMN_SHORTCUT_ID);
return map;
}
/**
* Returns a Cursor positioned at the word specified by rowId
*
* #param rowId id of word to retrieve
* #param columns The columns to include, if null then all are included
* #return Cursor positioned to matching word, or null if not found.
*/
public Cursor getWord(String rowId, String[] columns) {
String selection = "rowid = ?";
String[] selectionArgs = new String[] {rowId};
return query(selection, selectionArgs, columns);
/* This builds a query that looks like:
* SELECT <columns> FROM <table> WHERE rowid = <rowId>
*/
}
/**
* Returns a Cursor over all words that match the given query
*
* #param query The string to search for
* #param columns The columns to include, if null then all are included
* #return Cursor over all words that match, or null if none found.
*/
public Cursor getWordMatches(String query, String[] columns) {
String selection = KEY_WORD + " MATCH ?";
String[] selectionArgs = new String[] {query+"*"};
return query(selection, selectionArgs, columns);
/* This builds a query that looks like:
* SELECT <columns> FROM <table> WHERE <KEY_WORD> MATCH 'query*'
* which is an FTS3 search for the query text (plus a wildcard) inside the word column.
*
* - "rowid" is the unique id for all rows but we need this value for the "_id" column in
* order for the Adapters to work, so the columns need to make "_id" an alias for "rowid"
* - "rowid" also needs to be used by the SUGGEST_COLUMN_INTENT_DATA alias in order
* for suggestions to carry the proper intent data.
* These aliases are defined in the DictionaryProvider when queries are made.
* - This can be revised to also search the definition text with FTS3 by changing
* the selection clause to use FTS_VIRTUAL_TABLE instead of KEY_WORD (to search across
* the entire table, but sorting the relevance could be difficult.
*/
}
/**
* Performs a database query.
* #param selection The selection clause
* #param selectionArgs Selection arguments for "?" components in the selection
* #param columns The columns to return
* #return A Cursor over all rows matching the query
*/
private Cursor query(String selection, String[] selectionArgs, String[] columns) {
/* The SQLiteBuilder provides a map for all possible columns requested to
* actual columns in the database, creating a simple column alias mechanism
* by which the ContentProvider does not need to know the real column names
*/
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables(FTS_VIRTUAL_TABLE);
builder.setProjectionMap(mColumnMap);
Cursor cursor = builder.query(mDatabaseOpenHelper.getReadableDatabase(),
columns, selection, selectionArgs, null, null, null);
if (cursor == null) {
return null;
} else if (!cursor.moveToFirst()) {
cursor.close();
return null;
}
return cursor;
}
/**
* This creates/opens the database.
*/
private static class DictionaryOpenHelper extends SQLiteOpenHelper {
private final Context mHelperContext;
private SQLiteDatabase mDatabase;
/* Note that FTS3 does not support column constraints and thus, you cannot
* declare a primary key. However, "rowid" is automatically used as a unique
* identifier, so when making requests, we will use "_id" as an alias for "rowid"
*/
private static final String FTS_TABLE_CREATE =
"CREATE VIRTUAL TABLE " + FTS_VIRTUAL_TABLE +
" USING fts3 (" +
KEY_WORD + ", " +
KEY_DEFINITION + ", " +
KEY_EXAMPLE + " );";
DictionaryOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mHelperContext = context;
}
#Override
public void onCreate(SQLiteDatabase db) {
mDatabase = db;
mDatabase.execSQL(FTS_TABLE_CREATE);
loadDictionary();
}
/**
* Starts a thread to load the database table with words
*/
private void loadDictionary() {
new Thread(new Runnable() {
public void run() {
try {
loadWords();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}).start();
}
private void loadWords() throws IOException {
Log.d(TAG, "Loading words...");
final Resources resources = mHelperContext.getResources();
InputStream inputStream = resources.openRawResource(R.raw.definitions);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
try {
String line;
while ((line = reader.readLine()) != null) {
String[] strings = TextUtils.split(line, "//");
if (strings.length < 2) continue;
long id = addWord(strings[0].trim(), strings[1].trim(), strings[2].trim());
if (id < 0) {
Log.e(TAG, "unable to add word: " + strings[0].trim());
}
}
} finally {
reader.close();
}
Log.d(TAG, "DONE loading words.");
}
/**
* Add a word to the dictionary.
* #return rowId or -1 if failed
*/
public long addWord(String word, String definition, String example) {
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_WORD, word);
initialValues.put(KEY_DEFINITION, definition);
initialValues.put(KEY_EXAMPLE, example);
return mDatabase.insert(FTS_VIRTUAL_TABLE, null, initialValues);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS " + FTS_VIRTUAL_TABLE);
onCreate(db);
}
}
}
DictionaryProvider.java
public class DictionaryProvider extends ContentProvider {
String TAG = "DictionaryProvider";
public static String AUTHORITY = "com.example.android.searchabledict.DictionaryProvider";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/dictionary");
// MIME types used for searching words or looking up a single definition
public static final String WORDS_MIME_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE +
"/vnd.example.android.searchabledict";
public static final String DEFINITION_MIME_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE +
"/vnd.example.android.searchabledict";
private DictionaryDatabase mDictionary;
// UriMatcher stuff
private static final int SEARCH_WORDS = 0;
private static final int GET_WORD = 1;
private static final int SEARCH_SUGGEST = 2;
private static final int REFRESH_SHORTCUT = 3;
private static final UriMatcher sURIMatcher = buildUriMatcher();
/**
* Builds up a UriMatcher for search suggestion and shortcut refresh queries.
*/
private static UriMatcher buildUriMatcher() {
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
// to get definitions...
matcher.addURI(AUTHORITY, "dictionary", SEARCH_WORDS);
matcher.addURI(AUTHORITY, "dictionary/#", GET_WORD);
// to get suggestions...
matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);
/* The following are unused in this implementation, but if we include
* {#link SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} as a column in our suggestions table, we
* could expect to receive refresh queries when a shortcutted suggestion is displayed in
* Quick Search Box, in which case, the following Uris would be provided and we
* would return a cursor with a single item representing the refreshed suggestion data.
*/
matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT, REFRESH_SHORTCUT);
matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*", REFRESH_SHORTCUT);
return matcher;
}
#Override
public boolean onCreate() {
mDictionary = new DictionaryDatabase(getContext());
return true;
}
/**
* Handles all the dictionary searches and suggestion queries from the Search Manager.
* When requesting a specific word, the uri alone is required.
* When searching all of the dictionary for matches, the selectionArgs argument must carry
* the search query as the first element.
* All other arguments are ignored.
*/
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// Use the UriMatcher to see what kind of query we have and format the db query accordingly
switch (sURIMatcher.match(uri)) {
case SEARCH_SUGGEST:
if (selectionArgs == null) {
throw new IllegalArgumentException(
"selectionArgs must be provided for the Uri: " + uri);
}
return getSuggestions(selectionArgs[0]);
case SEARCH_WORDS:
if (selectionArgs == null) {
throw new IllegalArgumentException(
"selectionArgs must be provided for the Uri: " + uri);
}
return search(selectionArgs[0]);
case GET_WORD:
return getWord(uri);
case REFRESH_SHORTCUT:
return refreshShortcut(uri);
default:
throw new IllegalArgumentException("Unknown Uri: " + uri);
}
}
private Cursor getSuggestions(String query) {
query = query.toLowerCase();
String[] columns = new String[] {
BaseColumns._ID,
DictionaryDatabase.KEY_WORD,
/* SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
(only if you want to refresh shortcuts) */
SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID};
return mDictionary.getWordMatches(query, columns);
}
private Cursor search(String query) {
query = query.toLowerCase();
String[] columns = new String[] {
BaseColumns._ID,
DictionaryDatabase.KEY_WORD,
DictionaryDatabase.KEY_DEFINITION,
DictionaryDatabase.KEY_EXAMPLE};
return mDictionary.getWordMatches(query, columns);
}
private Cursor getWord(Uri uri) {
String rowId = uri.getLastPathSegment();
String[] columns = new String[] {
DictionaryDatabase.KEY_WORD,
DictionaryDatabase.KEY_DEFINITION,
DictionaryDatabase.KEY_EXAMPLE};
return mDictionary.getWord(rowId, columns);
}
private Cursor refreshShortcut(Uri uri) {
/* This won't be called with the current implementation, but if we include
* {#link SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} as a column in our suggestions table, we
* could expect to receive refresh queries when a shortcutted suggestion is displayed in
* Quick Search Box. In which case, this method will query the table for the specific
* word, using the given item Uri and provide all the columns originally provided with the
* suggestion query.
*/
String rowId = uri.getLastPathSegment();
String[] columns = new String[] {
BaseColumns._ID,
DictionaryDatabase.KEY_WORD,
DictionaryDatabase.KEY_DEFINITION,
DictionaryDatabase.KEY_EXAMPLE,
SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID};
return mDictionary.getWord(rowId, columns);
}
/**
* This method is required in order to query the supported types.
* It's also useful in our own query() method to determine the type of Uri received.
*/
#Override
public String getType(Uri uri) {
switch (sURIMatcher.match(uri)) {
case SEARCH_WORDS:
return WORDS_MIME_TYPE;
case GET_WORD:
return DEFINITION_MIME_TYPE;
case SEARCH_SUGGEST:
return SearchManager.SUGGEST_MIME_TYPE;
case REFRESH_SHORTCUT:
return SearchManager.SHORTCUT_MIME_TYPE;
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
}
// Other required implementations...
#Override
public Uri insert(Uri uri, ContentValues values) {
throw new UnsupportedOperationException();
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException();
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException();
}
}
SearchableDictionary.java
public class SearchableDictionary extends Activity {
private TextView mTextView;
private ListView mListView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mTextView = (TextView) findViewById(R.id.text);
mListView = (ListView) findViewById(R.id.list);
handleIntent(getIntent());
}
#Override
protected void onNewIntent(Intent intent) {
// Because this activity has set launchMode="singleTop", the system calls this method
// to deliver the intent if this activity is currently the foreground activity when
// invoked again (when the user executes a search from this activity, we don't create
// a new instance of this activity, so the system delivers the search intent here)
handleIntent(intent);
}
private void handleIntent(Intent intent) {
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
// handles a click on a search suggestion; launches activity to show word
Intent wordIntent = new Intent(this, WordActivity.class);
wordIntent.setData(intent.getData());
startActivity(wordIntent);
} else if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
// handles a search query
String query = intent.getStringExtra(SearchManager.QUERY);
showResults(query);
}
}
/**
* Searches the dictionary and displays results for the given query.
* #param query The search query
*/
private void showResults(String query) {
Cursor cursor = managedQuery(DictionaryProvider.CONTENT_URI, null, null,
new String[] {query}, null);
if (cursor == null) {
// There are no results
mTextView.setText(getString(R.string.no_results, new Object[] {query}));
} else {
// Display the number of results
int count = cursor.getCount();
String countString = getResources().getQuantityString(R.plurals.search_results,
count, new Object[] {count, query});
mTextView.setText(countString);
// Specify the columns we want to display in the result
String[] from = new String[] { DictionaryDatabase.KEY_WORD,
DictionaryDatabase.KEY_DEFINITION,
DictionaryDatabase.KEY_EXAMPLE};
// Specify the corresponding layout elements where we want the columns to go
int[] to = new int[] { R.id.word,
R.id.definition,
R.id.example};
// Create a simple cursor adapter for the definitions and apply them to the ListView
SimpleCursorAdapter words = new SimpleCursorAdapter(this,
R.layout.result, cursor, from, to);
mListView.setAdapter(words);
// Define the on-click listener for the list items
mListView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Build the Intent used to open WordActivity with a specific word Uri
Intent wordIntent = new Intent(getApplicationContext(), WordActivity.class);
Uri data = Uri.withAppendedPath(DictionaryProvider.CONTENT_URI,
String.valueOf(id));
wordIntent.setData(data);
startActivity(wordIntent);
}
});
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.options_menu, menu);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setIconifiedByDefault(false);
}
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.search:
onSearchRequested();
return true;
default:
return false;
}
}
}
WordActivity.java
public class WordActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.word);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
}
Uri uri = getIntent().getData();
Cursor cursor = managedQuery(uri, null, null, null, null);
if (cursor == null) {
finish();
} else {
cursor.moveToFirst();
TextView word = (TextView) findViewById(R.id.word);
TextView definition = (TextView) findViewById(R.id.definition);
TextView example = (TextView) findViewById(R.id.example);
int wIndex = cursor.getColumnIndexOrThrow(DictionaryDatabase.KEY_WORD);
int dIndex = cursor.getColumnIndexOrThrow(DictionaryDatabase.KEY_DEFINITION);
int eIndex = cursor.getColumnIndexOrThrow(DictionaryDatabase.KEY_EXAMPLE);
word.setText(cursor.getString(wIndex));
definition.setText(cursor.getString(dIndex));
example.setText(cursor.getString(eIndex));
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.options_menu, menu);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setIconifiedByDefault(false);
}
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.search:
onSearchRequested();
return true;
case android.R.id.home:
Intent intent = new Intent(this, SearchableDictionary.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return true;
default:
return false;
}
}
}
Close inspection reveals that you have the same string value assigned to 1 and 3
public static final String KEY_WORD = SearchManager.SUGGEST_COLUMN_TEXT_1;
public static final String KEY_DEFINITION = SearchManager.SUGGEST_COLUMN_TEXT_2;
public static final String KEY_EXAMPLE = SearchManager.SUGGEST_COLUMN_TEXT_1;
it is a limitation that you can have only two items in the search suggestion provided by android
refer section
The biggest problem with search suggestions
in http://www.grokkingandroid.com/android-tutorial-adding-suggestions-to-search/

Android: Refresh ListFragement using ContentProvider and Loader

I am using a SQLite database and a ContentProvider to fill a ListFragment. The problem is that ListFragment is not getting refreshed after I add a item. The ListFragment is empty. I have to close and reopen the app to show the added item in the list.
I try to update it like this:
public class RoomListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
//adapter using SQLite and ContentProvider to fill ListFragment
private SimpleCursorAdapter dataAdapter;
//needed for create room dialog
private EditText enter_room;
private static View textEntryView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//display ActionBar items
setHasOptionsMenu(true);
// TODO: replace with a real list adapter.
displayListView();
}
#Override
public void onResume() {
super.onResume();
//Starts a new or restarts an existing Loader in this manager
getLoaderManager().restartLoader(0, null, this);
}
#Override
public void onDestroy() {
super.onDestroy();
}
private void displayListView() {
// The desired columns to be bound
String[] columns = new String[] {
Database.KEY_GROUPADDRESS,
Database.KEY_NAME,
Database.KEY_DPT
};
// the XML defined views which the data will be bound to
int[] to = new int[] {
R.id.groupaddress,
R.id.name,
R.id.dpt,
};
// create an adapter from the SimpleCursorAdapter
dataAdapter = new SimpleCursorAdapter(
getActivity(),
R.layout.device_info,
null,
columns,
to,
0);
//set SimpleCursorAdapter to ListFragmentAdapter
setListAdapter(dataAdapter);
//Ensures a loader is initialized and active.
getLoaderManager().initLoader(0, null, this);
}
// This is called when a new Loader needs to be created.
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = {
Database.KEY_ROWID,
Database.KEY_GROUPADDRESS,
Database.KEY_NAME,
Database.KEY_DPT};
CursorLoader cursorLoader = new CursorLoader(getActivity(),
MyContentProvider.CONTENT_URI, projection, null, null, null);
return cursorLoader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
dataAdapter.swapCursor(data);
dataAdapter.notifyDataSetChanged();
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
dataAdapter.swapCursor(null);
}
I add a item with this code:
//Handle OnClick events on ActionBar items
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// handle item selection
switch (item.getItemId()) {
case R.id.menu_add:
//Toast.makeText(getActivity(), "Click", Toast.LENGTH_SHORT).show();
LayoutInflater factory = LayoutInflater.from(getActivity());
//textEntryView is an Layout XML file containing text field to display in alert dialog
textEntryView = factory.inflate(R.layout.dialog_add_room, null);
//get the control from the layout
enter_room = (EditText) textEntryView.findViewById(R.id.enter_room);
//create Dialog
final AlertDialog.Builder alert1 = new AlertDialog.Builder(getActivity());
//configure dialog
alert1.setTitle("Raum hinzufügen:").setView(textEntryView)
.setPositiveButton("Hinzufügen",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
String roomname = enter_room.getText().toString();
Log.d("Insert: ", "Inserting ..");
ContentValues values = new ContentValues();
//TODO Richtige Spalte für Raumname verwenden
values.put(Database.KEY_NAME, roomname);
getActivity().getContentResolver().insert(MyContentProvider.CONTENT_URI, values);
dataAdapter.notifyDataSetChanged();
}
}).setNegativeButton("Abbrechen",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
//cancel dialog
}
});
alert1.show();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
My ContentProvider:
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
public class MyContentProvider extends ContentProvider{
private MyDatabaseHelper dbHelper;
private static final int ALL_COUNTRIES = 1;
private static final int SINGLE_COUNTRY = 2;
// authority is the symbolic name of your provider
// To avoid conflicts with other providers, you should use
// Internet domain ownership (in reverse) as the basis of your provider authority.
private static final String AUTHORITY = "de.mokkapps.fixknxdemo.contentprovider";
// create content URIs from the authority by appending path to database table
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/countries");
// a content URI pattern matches content URIs using wildcard characters:
// *: Matches a string of any valid characters of any length.
// #: Matches a string of numeric characters of any length.
private static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "countries", ALL_COUNTRIES);
uriMatcher.addURI(AUTHORITY, "countries/#", SINGLE_COUNTRY);
}
// system calls onCreate() when it starts up the provider.
#Override
public boolean onCreate() {
// get access to the database helper
dbHelper = new MyDatabaseHelper(getContext());
return false;
}
//Return the MIME type corresponding to a content URI
#Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case ALL_COUNTRIES:
return "vnd.android.cursor.dir/vnd.com.as400samplecode.contentprovider.countries";
case SINGLE_COUNTRY:
return "vnd.android.cursor.item/vnd.com.as400samplecode.contentprovider.countries";
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
}
// The insert() method adds a new row to the appropriate table, using the values
// in the ContentValues argument. If a column name is not in the ContentValues argument,
// you may want to provide a default value for it either in your provider code or in
// your database schema.
#Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
switch (uriMatcher.match(uri)) {
case ALL_COUNTRIES:
//do nothing
break;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
long id = db.insert(Database.SQLITE_TABLE, null, values);
getContext().getContentResolver().notifyChange(uri, null);
return Uri.parse(CONTENT_URI + "/" + id);
}
// The query() method must return a Cursor object, or if it fails,
// throw an Exception. If you are using an SQLite database as your data storage,
// you can simply return the Cursor returned by one of the query() methods of the
// SQLiteDatabase class. If the query does not match any rows, you should return a
// Cursor instance whose getCount() method returns 0. You should return null only
// if an internal error occurred during the query process.
#Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(Database.SQLITE_TABLE);
switch (uriMatcher.match(uri)) {
case ALL_COUNTRIES:
//do nothing
break;
case SINGLE_COUNTRY:
String id = uri.getPathSegments().get(1);
queryBuilder.appendWhere(Database.KEY_ROWID + "=" + id);
break;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
Cursor cursor = queryBuilder.query(db, projection, selection,
selectionArgs, null, null, sortOrder);
return cursor;
}
// The delete() method deletes rows based on the selection or if an id is
// provided then it deleted a single row. The methods returns the numbers
// of records delete from the database. If you choose not to delete the data
// physically then just update a flag here.
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
switch (uriMatcher.match(uri)) {
case ALL_COUNTRIES:
//do nothing
break;
case SINGLE_COUNTRY:
String id = uri.getPathSegments().get(1);
selection = Database.KEY_ROWID + "=" + id
+ (!TextUtils.isEmpty(selection) ?
" AND (" + selection + ')' : "");
break;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
int deleteCount = db.delete(Database.SQLITE_TABLE, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
return deleteCount;
}
// The update method() is same as delete() which updates multiple rows
// based on the selection or a single row if the row id is provided. The
// update method returns the number of updated rows.
#Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
switch (uriMatcher.match(uri)) {
case ALL_COUNTRIES:
//do nothing
break;
case SINGLE_COUNTRY:
String id = uri.getPathSegments().get(1);
selection = Database.KEY_ROWID + "=" + id
+ (!TextUtils.isEmpty(selection) ?
" AND (" + selection + ')' : "");
break;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
int updateCount = db.update(Database.SQLITE_TABLE, values, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
return updateCount;
}
}
You should call notifyDataSetChanged(); from within your content providers' insert method and supply the URI as an argument.
At the point in time you are currently calling the notifyDataSetChanged(); method the insert may not have actually happened as the content provider call to insert will be handled asynchronously.
An example could look something like this
#Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase sqlDB = mDB.getWritableDatabase();
int uriType = sURIMatcher.match(uri);
long id;
switch (uriType) {
case TEAMS:
id = sqlDB.replace(TeamModel.TEAM_TABLE_NAME, null, values);
break;
case CARS:
id = sqlDB.replace(CarModel.CAR_TABLE_NAME, null, values);
break;
case TEAM_ERRORS:
id = sqlDB.replace(TeamErrorModel.TEAMS_ERRORS_TABLE_NAME, null, values);
String teamId = values.get(TeamErrorModel.COL_TEAM_ID).toString();
String selection = TeamModel.COL_ID + " = ?";
String[] selectionArgs = {teamId};
setErrorFlagTeamModel(sqlDB, true, selection, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null, false);
return Uri.parse(uri + "/" + id);
}
The first argument is the URI passed in to the insert method and will tell ALL adapters listening in on that particular uri to update their data.
The last argument (false) tells a sync adapter to ignore this change. I assume you are not using a sync adapter
All methods in your ContentProvider should call the notifyChange method in a similar way.
You may well find that the insert actually failed. so check that the records are actually being inserted.
UPDATE
As per comment below from #zapi
And you need to add cursor.setNotificationUri(contentresolver, uri)
inside the query method or the Cursor does not know for which uri
notification it has to listen
Since answering your question you have posted your content provider and I can now see that in fact as per the above quote this is in fact your missing link

Categories

Resources