I am creating an app using SQLite.
It crashes before loading.
I fixed all viable errors and since I do not even get a log info, I am having troubles figuring out the error.
Please help.
SQLHelper
public class MySQLiteHelper extends SQLiteOpenHelper {
// Database Version
private static final int DATABASE_VERSION = 1;
// Database Name
private static final String DATABASE_NAME = "MediaDB";
// Media table name
private static final String TABLE_MEDIA = "media";
// Media Table Columns names
private static final String KEY_ID = "id";
private static final String KEY_TYPE = "type";
private static final String KEY_TITLE = "title";
private static final String KEY_AUTHOR = "author";
private static final String[] COLUMNS = {KEY_ID,KEY_TYPE,KEY_TITLE,KEY_AUTHOR};
public MySQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
// SQL statement to create media table
String CREATE_MEDIA_TABLE = "CREATE TABLE media ( " +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"tyoe TYPE, "+
"title TEXT, "+
"author TEXT )";
// create media table
db.execSQL(CREATE_MEDIA_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Drop older media table if existed
db.execSQL("DROP TABLE IF EXISTS media");
// create fresh media table
this.onCreate(db);
}
//ADD MEDIA
public void addMedia(Media media){
//for logging
Log.d("addMedia", media.toString());
// get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
// create ContentValues to add key "column"/value
ContentValues values = new ContentValues();
values.put(KEY_TYPE, media.getType()); // get title
values.put(KEY_TITLE, media.getTitle()); // get title
values.put(KEY_AUTHOR, media.getAuthor()); // get author
// insert
db.insert(TABLE_MEDIA, // table
null, //nullColumnHack
values); // key/value -> keys = column names/ values = column values
// close
db.close();
}
//GET MEDIA
public Media getMedia(int id){
// get reference to readable DB
SQLiteDatabase db = this.getReadableDatabase();
// build query
Cursor cursor =
db.query(TABLE_MEDIA, // table
COLUMNS, // column names
" id = ?", // selections
new String[] { String.valueOf(id) }, // d. selections args
null, // group by
null, // having
null, // order by
null); // limit
// if we got results get the first one
if (cursor != null)
cursor.moveToFirst();
// build media object
Media media = new Media();
media.setId(Integer.parseInt(cursor.getString(0)));
media.setType(cursor.getString(1));
media.setTitle(cursor.getString(2));
media.setAuthor(cursor.getString(3));
//log
Log.d("getMedia("+id+")", media.toString());
// return media
return media;
}
//GET ALL MEDIA
public List<Media> getAllMedia() {
List<Media> medias = new LinkedList<Media>();
// build the query
String query = "SELECT * FROM " + TABLE_MEDIA;
// get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(query, null);
// go over each row, build media and add it to list
Media media = null;
if (cursor.moveToFirst()) {
do {
media = new Media();
media.setId(Integer.parseInt(cursor.getString(0)));
media.setType(cursor.getString(1));
media.setTitle(cursor.getString(2));
media.setAuthor(cursor.getString(3));
// Add media to media
medias.add(media);
} while (cursor.moveToNext());
}
Log.d("getAllMedia()", medias.toString());
// return media
return medias;
}
//UPDATE
public int updateMedia(Media media) {
// get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
// create ContentValues to add key "column"/value
ContentValues values = new ContentValues();
values.put("type", media.getType()); // get title
values.put("title", media.getTitle()); // get title
values.put("author", media.getAuthor()); // get author
// updating row
int i = db.update(TABLE_MEDIA, //table
values, // column/value
KEY_ID+" = ?", // selections
new String[] { String.valueOf(media.getId()) }); //selection args
// close
db.close();
return i;
}
//DELETE
public void deleteMedia(Media media) {
// get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
// delete
db.delete(TABLE_MEDIA, //table name
KEY_ID+" = ?", // selections
new String[] { String.valueOf(media.getId()) }); //selections args
// close
db.close();
//log
Log.d("deleteMedia", media.toString());
}
}
Media object class
public class Media {
private int id;
private String type;
private String title;
private String author;
public Media(){}
public Media(String type, String title, String author) {
super();
this.type = type;
this.title = title;
this.author = author;
}
//getters & setters
// getting ID
public int getId(){
return this.id;
}
// setting id
public void setId(int id){
this.id = id;
}
// getting type
public String getType(){
return this.type;
}
// setting title
public void setType(String type){
this.type = type;
}
// getting title
public String getTitle(){
return this.title;
}
// setting title
public void setTitle(String title){
this.title = title;
}
// getting author
public String getAuthor(){
return this.author;
}
// setting author
public void setAuthor(String author){
this.author = author;
}
#Override
public String toString() {
return "Media [id=" + id + ", type=" + type + ",title=" + title + ", author=" + author
+ "]";
}
}
Main Activity
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MySQLiteHelper db = new MySQLiteHelper(this);
/**
* CRUD Operations
* */
// add Media
db.addMedia(new Media("Book", "Android Application Development Cookbook", "Wei Meng Lee"));
db.addMedia(new Media("Book", "Android Programming: The Big Nerd Ranch Guide", "Bill Phillips and Brian Hardy"));
db.addMedia(new Media("Book", "Learn Android App Development", "Wallace Jackson"));
// get all media
List<Media> list = db.getAllMedia();
// delete one media
db.deleteMedia(list.get(0));
// get all media
db.getAllMedia();
}
}
There's an error in you table creation:
String CREATE_MEDIA_TABLE = "CREATE TABLE media ( " +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"tyoe TYPE, "+
"title TEXT, "+
"author TEXT )";
TYPE is not a valid SQLlite Data Type.
Please refer to this page: https://www.sqlite.org/datatype3.html
I'd write your table creation as
String CREATE_MEDIA_TABLE = "CREATE TABLE media (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"tyoe TEXT, "+
"title TEXT, "+
"author TEXT)";
Related
I'm upgrading some legacy to target Android Q, and of course this code stop working:
String[] PROJECTION_BUCKET = {MediaStore.Images.ImageColumns.BUCKET_ID,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.DATA,
"COUNT(" + MediaStore.Images.ImageColumns._ID + ") AS COUNT",
MediaStore.Files.FileColumns.MEDIA_TYPE,
MediaStore.MediaColumns._ID};
String BUCKET_GROUP_BY = " 1) and " + BUCKET_WHERE.toString() + " GROUP BY 1,(2";
cur = context.getContentResolver().query(images, PROJECTION_BUCKET,
BUCKET_GROUP_BY, null, BUCKET_ORDER_BY);
android.database.sqlite.SQLiteException: near "GROUP": syntax error (code 1 SQLITE_ERROR[1])
Here it supposed to obtain list of images with album name, date, count of pictures - one image for each album, so we can create album picker screen without querying all pictures and loop through it to create albums.
Is it possible to group query results with contentResolver since SQL queries stoped work?
(I know that ImageColumns.DATA and "COUNT() AS COUNT" are deprecated too, but this is a question about GROUP BY)
(There is a way to query albums and separately query photo, to obtain photo uri for album cover, but i want to avoid overheads)
Unfortunately Group By is no longer supported in Android 10 and above, neither any aggregated functions such as COUNT. This is by design and there is no workaround.
The solution is what you are actually trying to avoid, which is to query, iterate, and get metrics.
To get you started you can use the next snipped, which will resolve the buckets (albums), and the amount of records in each one.
I haven't added code to resolve the thumbnails, but is easy. You must perform a query for each bucket Id from all the Album instances, and use the image from the first record.
public final class AlbumQuery
{
#NonNull
public static HashMap<String, AlbumQuery.Album> get(#NonNull final Context context)
{
final HashMap<String, AlbumQuery.Album> output = new HashMap<>();
final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
final String[] projection = {MediaStore.Images.Media.BUCKET_DISPLAY_NAME, MediaStore.Images.Media.BUCKET_ID};
try (final Cursor cursor = context.getContentResolver().query(contentUri, projection, null, null, null))
{
if ((cursor != null) && (cursor.moveToFirst() == true))
{
final int columnBucketName = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
final int columnBucketId = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_ID);
do
{
final String bucketId = cursor.getString(columnBucketId);
final String bucketName = cursor.getString(columnBucketName);
if (output.containsKey(bucketId) == false)
{
final int count = AlbumQuery.getCount(context, contentUri, bucketId);
final AlbumQuery.Album album = new AlbumQuery.Album(bucketId, bucketName, count);
output.put(bucketId, album);
}
} while (cursor.moveToNext());
}
}
return output;
}
private static int getCount(#NonNull final Context context, #NonNull final Uri contentUri, #NonNull final String bucketId)
{
try (final Cursor cursor = context.getContentResolver().query(contentUri,
null, MediaStore.Images.Media.BUCKET_ID + "=?", new String[]{bucketId}, null))
{
return ((cursor == null) || (cursor.moveToFirst() == false)) ? 0 : cursor.getCount();
}
}
public static final class Album
{
#NonNull
public final String buckedId;
#NonNull
public final String bucketName;
public final int count;
Album(#NonNull final String bucketId, #NonNull final String bucketName, final int count)
{
this.buckedId = bucketId;
this.bucketName = bucketName;
this.count = count;
}
}
}
This is a more efficient(not perfect) way to do that.
I am doing it for videos, but doing so is the same for images to. just change MediaStore.Video.Media.X to MediaStore.Images.Media.X
public class QUtils {
/*created by Nasib June 6, 2020*/
#RequiresApi(api = Build.VERSION_CODES.Q)
public static ArrayList<FolderHolder> loadListOfFolders(Context context) {
ArrayList<FolderHolder> allFolders = new ArrayList<>();//list that we need
HashMap<Long, String> folders = new HashMap<>(); //hashmap to track(no duplicates) folders by using their ids
String[] projection = {MediaStore.Video.Media._ID,
MediaStore.Video.Media.BUCKET_ID,
MediaStore.Video.Media.BUCKET_DISPLAY_NAME,
MediaStore.Video.Media.DATE_ADDED};
ContentResolver CR = context.getContentResolver();
Uri root = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
Cursor c = CR.query(root, projection, null, null, MediaStore.Video.Media.DATE_ADDED + " desc");
if (c != null && c.moveToFirst()) {
int folderIdIndex = c.getColumnIndexOrThrow(MediaStore.Video.Media.BUCKET_ID);
int folderNameIndex = c.getColumnIndexOrThrow(MediaStore.Video.Media.BUCKET_DISPLAY_NAME);
int thumbIdIndex = c.getColumnIndexOrThrow(MediaStore.Video.Media._ID);
int dateAddedIndex = c.getColumnIndexOrThrow(MediaStore.Video.Media.DATE_ADDED);
do {
Long folderId = c.getLong(folderIdIndex);
if (folders.containsKey(folderId) == false) { //proceed only if the folder data has not been inserted already :)
long thumbId = c.getLong(thumbIdIndex);
String folderName = c.getString(folderNameIndex);
String dateAdded = c.getString(dateAddedIndex);
Uri thumbPath = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, thumbId);
folders.put(folderId, folderName);
allFolders.add(new FolderHolder(String.valueOf(thumbPath), folderName, dateAdded));
}
} while (c.moveToNext());
c.close(); //close cursor
folders.clear(); //clear the hashmap becuase it's no more useful
}
return allFolders;
}
}
FolderHolder model class
public class FolderHolder {
private String folderName;
public long dateAdded;
private String thumbnailPath;
public long folderId;
public void setPath(String thumbnailPath) {
this.thumbnailPath = thumbnailPath;
}
public String getthumbnailPath() {
return thumbnailPath;
}
public FolderHolder(long folderId, String thumbnailPath, String folderName, long dateAdded) {
this.folderId = folderId;
this.folderName = folderName;
this.thumbnailPath = thumbnailPath;
this.dateAdded = dateAdded;
}
public String getFolderName() {
return folderName;
}
}
GROUP_BY supporting in case of using Bundle:
val bundle = Bundle().apply {
putString(
ContentResolver.QUERY_ARG_SQL_SORT_ORDER,
"${MediaStore.MediaColumns.DATE_MODIFIED} DESC"
)
putString(
ContentResolver.QUERY_ARG_SQL_GROUP_BY,
MediaStore.Images.ImageColumns.BUCKET_ID
)
}
contentResolver.query(
uri,
arrayOf(
MediaStore.Images.ImageColumns.BUCKET_ID,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.DATA
),
bundle,
null
)
I need to implement an update of the database lying in the assets. User data, namely, in the "favorite" record or not, should be saved.
I already asked a question and they helped me -https://stackoverflow.com/a/53827525/10261947
Everything worked in a test application. But when I transferred the code (exactly the same) to the real application, an error occurs - E/AndroidRuntime: FATAL EXCEPTION: ModernAsyncTask #1
Process: rodionova.lyubov.brodsky, PID: 4196
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.support.v4.content.ModernAsyncTask$3.done(ModernAsyncTask.java:161)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
at java.util.concurrent.FutureTask.run(FutureTask.java:271)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:784)
Caused by: java.lang.IllegalArgumentException: the bind value at index 4 is null
at android.database.sqlite.SQLiteProgram.bindString(SQLiteProgram.java:169)
at android.database.sqlite.SQLiteProgram.bindAllArgsAsStrings(SQLiteProgram.java:205)
at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:47)
at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1397)
at android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.java:1239)
at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1110)
at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1278)
at rodionova.lyubov.brodsky.db.PoemsDbHelper.insertCorePoem(PoemsDbHelper.java:121)
at rodionova.lyubov.brodsky.db.PoemsDbHelper.getNewPoems(PoemsDbHelper.java:90)
at rodionova.lyubov.brodsky.db.PoemsDbHelper.onUpgrade(PoemsDbHelper.java:41)
at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.getWritableDatabase(SQLiteAssetHelper.java:197)
at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.getReadableDatabase(SQLiteAssetHelper.java:254)
at rodionova.lyubov.brodsky.db.PoemsProvider.query(PoemsProvider.java:45)
at android.content.ContentProvider.query(ContentProvider.java:1057)
If you do not perform the update, the application is working properly, so I will post only the code DbHelper
public class PoemsDbHelper extends SQLiteAssetHelper {
public static final String DB_NAME = "brodsky.db";
public static final int DBVERSION = 3;
public static final String TBLNAME = "poems_table";
public static final String COL_ID = "id";
public static final String COL_TITLE = "title";
public static final String COl_POEM = "poem";
public static final String COL_SUBJECT = "subject";
public static final String COL_YEARS = "years";
public static final String COL_FAVOURITE = "favorite";
Context mContext;
public PoemsDbHelper(Context context) {
super(context, DB_NAME, null, DBVERSION);
mContext = context;
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if(newVersion > oldVersion)
getNewPoems(mContext, db);
}
private void getNewPoems(Context context, SQLiteDatabase db) {
InputStream is;
OutputStream os;
final String tempNewDbName = "temp_brodsky.db";
int buffersize = 4096;
byte[] buffer = new byte[buffersize];
String newDBPath = mContext.getDatabasePath(tempNewDbName).getPath();
File newDBFile = new File(newDBPath);
if (newDBFile.exists()) {
newDBFile.delete();
}
File newDBFileDirectory = newDBFile.getParentFile();
if (!newDBFileDirectory.exists()) {
newDBFileDirectory.mkdirs();
}
try {
is = context.getAssets().open("databases/" + DB_NAME);
os = new FileOutputStream(newDBFile);
int bytes_read;
while ((bytes_read = is.read(buffer,0,buffersize)) > 0) {
os.write(buffer);
}
os.flush();
os.close();
is.close();
}catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Ouch updated database not copied - processing stopped - see stack-trace above.");
}
long id = maxid(db) + 1;
SQLiteDatabase newdb = SQLiteDatabase.openDatabase(newDBFile.getPath(),null,SQLiteDatabase.OPEN_READONLY);
Cursor csr = newdb.query(TBLNAME,null,null,null,null,null,null);
long insert_result;
db.beginTransaction();
while (csr.moveToNext()) {
insert_result = insertCorePoem(
db,
id,
csr.getString(csr.getColumnIndex(COL_TITLE)),
csr.getString(csr.getColumnIndex(COl_POEM)),
csr.getString(csr.getColumnIndex(COL_SUBJECT)),
csr.getString(csr.getColumnIndex(COL_YEARS)),
csr.getString(csr.getColumnIndex(COL_FAVOURITE))
);
if (insert_result > 0) {
id++;
}
}
db.setTransactionSuccessful();
db.endTransaction();
csr.close();
newDBFile.delete();
}
public long insertCorePoem(SQLiteDatabase db, long id, String title, String poem, String subject, String years, String favourite) {
String whereclause = COL_TITLE + "=? AND " + COl_POEM + "=? AND " + COL_SUBJECT + "=? AND " + COL_YEARS + "=?";
String[] whereargs = new String[]{
title,
poem,
subject,
years
};
Cursor csr = db.query(TBLNAME,null,whereclause,whereargs,null,null,null);
boolean rowexists = (csr.getCount() > 0);
csr.close();
if (rowexists) {
Log.d("INSERTCOREPOEM","Skipping insert of row");
return -2;
}
ContentValues cv = new ContentValues();
cv.put(COL_ID,id);
cv.put(COL_TITLE,title);
cv.put(COl_POEM,poem);
cv.put(COL_SUBJECT,subject);
cv.put(COL_YEARS,years);
cv.put(COL_FAVOURITE,favourite);
Log.d("INSERTCOREPOEM","Inserting new column with id " + String.valueOf(id));
return db.insert(TBLNAME, null, cv);
}
private long maxid(SQLiteDatabase db) {
long rv = 0;
String extractcolumn = "maxid";
String[] col = new String[]{"max(" + COL_ID + ") AS " + extractcolumn};
Cursor csr = db.query(TBLNAME,col,null,null,null,null,null);
if (csr.moveToFirst()) {
rv = csr.getLong(csr.getColumnIndex(extractcolumn));
}
csr.close();
return rv;
}
}
I do not understand what is wrong. Identical code works great friend application. I would be grateful for the help.
Your issue is that you likely have a value of null in the years column of a row or rows in the updated database that data is being copied from.
Although you could change the code to handle (skip insertion or use provide a year value) the end result may not be desired. So the most likely fix would be to amend the database to have valid/useful year values.
I am developing an application. When I modify my data, the previous data is displayed and not the updated data. This is the ModifyActivity.java file:
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
public class ModifyCountryActivity extends Activity implements OnClickListener {
private EditText titleText, dateText, timeText;
private Button updateBtn, deleteBtn;
public Calendars calendars;
private DatabaseHelper dbHelper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle("Modify Record");
setContentView(R.layout.activity_modify_record);
dbHelper = new DatabaseHelper(this);
calendars = new Calendars();
titleText = (EditText) findViewById(R.id.title_edittext_modify);
timeText = (EditText) findViewById(R.id.time_edittext_modify);
dateText = (EditText) findViewById(R.id.date_edittext_modify);
updateBtn = (Button) findViewById(R.id.btn_update);
deleteBtn = (Button) findViewById(R.id.btn_delete);
Intent intent = getIntent();
String title = intent.getStringExtra("title");
String time = intent.getStringExtra("time");
String date = intent.getStringExtra("date");
titleText.setText(title);
timeText.setText(time);
dateText.setText(date);
updateBtn.setOnClickListener(this);
deleteBtn.setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_update:
titleText.setText(titleText.getText().toString() + " ");
timeText.setText(dateText.getText().toString() + " ");
dateText.setText(timeText.getText().toString() + " ");
calendars.set_remindertitle(titleText.getText().toString() + " ");
calendars.set_reminderdate(dateText.getText().toString() + " ");
calendars.set_remindertime(timeText.getText().toString() + " ");
dbHelper.update(calendars);
this.returnHome();
break;
case R.id.btn_delete:
dbHelper.delete(calendars);
this.returnHome();
break;
}
}
public void returnHome() {
Intent home_intent = new Intent(getApplicationContext(), CountryListActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(home_intent);
}
}
The database doesn't update. It shows the previous data again. This is the database class:
public class DatabaseHelper extends SQLiteOpenHelper {
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "calendar.db";
public static final String TABLE_REMINDER = "reminder";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_REMINDER_TITLE = "_remindertitle";
public static final String COLUMN_REMINDER_DESCRIPTION = "_reminderdescription";
public static final String COLUMN_REMINDER_DATE = "_reminderdate";
public static final String COLUMN_REMINDER_TIME = "_remindertime";
public static final String COLUMN_REMINDER_REPEAT = "_reminderrepeat";
public static final String COLUMN_REMINDER_SNOOZE = "_remindersnooze";
SQLiteDatabase database;
// Database Information
Class<? extends DatabaseHelper> context = getClass();
DatabaseHelper dbHelper;
// Creating table query
public void onCreate(SQLiteDatabase db) {
String query = "CREATE TABLE " + TABLE_REMINDER + " (" +
COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
COLUMN_REMINDER_DATE + " TEXT, " + COLUMN_REMINDER_TIME + " TEXT, " + COLUMN_REMINDER_TITLE + " TEXT, "
+ COLUMN_REMINDER_DESCRIPTION + " TEXT, " + COLUMN_REMINDER_REPEAT + " TEXT, " + COLUMN_REMINDER_SNOOZE + " TEXT " + ");";
Log.i("Query", query);
db.execSQL(query);
}
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_REMINDER);
onCreate(db);
}
public ArrayList<Calendars> databaseToArrayList() {
ArrayList<Calendars> arrayList = new ArrayList();
SQLiteDatabase db = getWritableDatabase();
String query = "SELECT * FROM " + TABLE_REMINDER;
Cursor c = db.rawQuery(query, null);
c.moveToFirst();
while (!c.isAfterLast()) {
if (c.getString(c.getColumnIndex("_reminderdate")) != null) {
Calendars calendars = new Calendars();
calendars.set_reminderdate(c.getString(c.getColumnIndex(COLUMN_REMINDER_DATE)));
calendars.set_remindertime(c.getString(c.getColumnIndex(COLUMN_REMINDER_TIME)));
calendars.set_remindertitle(c.getString(c.getColumnIndex(COLUMN_REMINDER_TITLE)));
calendars.set_reminderdescription(c.getString(c.getColumnIndex(COLUMN_REMINDER_DESCRIPTION)));
calendars.set_reminderrepeat(c.getString(c.getColumnIndex(COLUMN_REMINDER_REPEAT)));
calendars.set_remindersnooze(c.getString(c.getColumnIndex(COLUMN_REMINDER_SNOOZE)));
arrayList.add(calendars);
}
c.moveToNext();
}
c.close();
db.close();
return arrayList;
}
public void remove(String id) {
String string = String.valueOf(id);
SQLiteDatabase database = getReadableDatabase();
database.execSQL("DELETE FROM " + TABLE_REMINDER + " WHERE _id = '" + string + "'");
}
public void addReminder(Calendars calendars) {
ContentValues contentValues = new ContentValues();
contentValues.put(COLUMN_REMINDER_DATE, calendars.get_reminderdate());
contentValues.put(COLUMN_REMINDER_TIME, calendars.get_remindertime());
contentValues.put(COLUMN_REMINDER_TITLE, calendars.get_remindertitle());
contentValues.put(COLUMN_REMINDER_DESCRIPTION, calendars.get_reminderdescription());
contentValues.put(COLUMN_REMINDER_REPEAT, calendars.get_reminderrepeat());
contentValues.put(COLUMN_REMINDER_SNOOZE, calendars.get_remindersnooze());
SQLiteDatabase database = getReadableDatabase();
database.insert(TABLE_REMINDER, null, contentValues);
Log.i("insData", "the data has been inseted");
database.close();
}
public Cursor fetch() {
String[] columns = new String[]{COLUMN_ID, /*COLUMN_REMINDER_DATE, COLUMN_REMINDER_TIME, COLUMN_REMINDER_TITLE,*/
COLUMN_REMINDER_DESCRIPTION, COLUMN_REMINDER_REPEAT, COLUMN_REMINDER_SNOOZE};
SQLiteDatabase database = getReadableDatabase();
Cursor cursor = database.query(TABLE_REMINDER, columns, null, null, null, null, null);
if (cursor != null)
{
cursor.moveToFirst();
}
return cursor;
}
public int update(Calendars calendars) {
ContentValues contentValues = new ContentValues();
contentValues.put(COLUMN_REMINDER_DATE, calendars.get_reminderdate());
contentValues.put(COLUMN_REMINDER_TIME, calendars.get_remindertime());
contentValues.put(COLUMN_REMINDER_TITLE, calendars.get_remindertitle());/*
contentValues.put(COLUMN_REMINDER_DESCRIPTION, calendars.get_reminderdescription());
contentValues.put(COLUMN_REMINDER_REPEAT, calendars.get_reminderrepeat());
contentValues.put(COLUMN_REMINDER_SNOOZE, calendars.get_remindersnooze());*/
SQLiteDatabase database = getReadableDatabase();
int i = database.update(TABLE_REMINDER, contentValues, COLUMN_ID + " = " + calendars.get_id(), null);
database.close();
return i;
}
public void delete(Calendars calendars) {
database = getReadableDatabase();
database.delete(TABLE_REMINDER, COLUMN_ID + "=" + calendars.get_id(), null);
}
}
I believe that the update button should be working fine. I am new to Android and don't know how to solve this problem. Any suggestions on how to solve it?
If you're update function returns an int, then in your onClick function, rather than typing:
dbHelper.update(calendars);
You need to type:
int update = dbHelper.update(calendars);
Or:
if (dbHelper.update(calendars) > 0) {
// do something here
}
I would recommend the latter of the options. See how you go.
Please see this
This is what I want to see... I mean the tool tip
At run time I get this error message for each row that is being loaded.
The tool tip text lies in the an object and is retrieved by thisRow.getCourseTootip(i);
Number of columns in the table varies and I create them and add them to the table view thru code.
for (int courseNo = 0; courseNo < numberOfCourses; courseNo++) {
String colName = getASemesterCourse(thisSemester, courseNo).getCourseID();
TableColumn<AResultRow, String> thisColumn = new TableColumn<>(colName);
thisColumn.setPrefWidth(80);
thisColumn.setStyle("-fx-alignment: CENTER; font-weight:bold;");
String str = TableRows.get(1).getGrade(courseNo);
final int i = courseNo;
thisColumn.setCellValueFactory(cellData -> cellData.getValue().courseGradeProperty(i));
thisColumn.setCellFactory(new Callback<TableColumn<AResultRow, String>, TableCell<AResultRow, String>>() {
public TableCell<AResultRow, String> call(TableColumn<AResultRow, String> column) {
return new TableCell<AResultRow, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setText(item);
AResultRow thisRow = new AResultRow();
thisRow = getTableView().getItems().get(getTableRow().getIndex());
final Tooltip tip= new Tooltip();
tip.setText(thisRow.getCourseTootip(i));
setTooltip(tip);
tip.setStyle("-fx-background-color: pink; -fx-text-fill: black; -fx-font: normal normal 12pt \"Times New Roman\"");
}
}
};
}
});
boolean retVal = thisTable.getColumns().addAll(thisColumn);
}
Error is
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at victoriairene.TheMainFXMLController$1$1.updateItem(TheMainFXMLController.java:434)
at victoriairene.TheMainFXMLController$1$1.updateItem(TheMainFXMLController.java:427)
Line 434 is
thisRow = getTableView().getItems().get(getTableRow().getIndex());
Text for the tool tip for this cell comes from thisRow.getCourseTootip(i).
Can someone tell me, what is wrong with my code? Which object is null ? If it is null, then how do I get to see the correct Tooltip text, in spite of getting error messages for each row ?
I have been struggling with this for one full day.
Please help and thanks in advance.
As requested by Kleopatra I am enclosing the entire Create Table function.
public void createTableForThisSemester(int thisSemester, int numberOfCourses, javafx.collections.ObservableList<AResultRow> TableRows) {
TableView<AResultRow> thisTable = new TableView<>();
thisTable.setContextMenu(contextMenu);
TableColumn<AResultRow, String> tcolRollNo = new TableColumn<>("Roll Number");
tcolRollNo.setEditable(false);
tcolRollNo.setPrefWidth(120);
TableColumn<AResultRow, String> tcolName = new TableColumn<>("Student Name");
tcolName.setEditable(false);
tcolName.setPrefWidth(350);
tcolRollNo.setCellValueFactory(cellData -> cellData.getValue().StudentIDProperty());
tcolName.setCellValueFactory(cellData -> cellData.getValue().StudentNameProperty());
boolean xyz = thisTable.getColumns().addAll(tcolRollNo, tcolName);
// TableColumn[] courseColumn = new TableColumn[numberOfCourses];
for (int courseNo = 0; courseNo < numberOfCourses; courseNo++) {
String colName = getASemesterCourse(thisSemester, courseNo).getCourseID();
TableColumn<AResultRow, String> thisColumn = new TableColumn<>(colName);
thisColumn.setPrefWidth(80);
thisColumn.setStyle("-fx-alignment: CENTER; font-weight:bold;");
String str = TableRows.get(1).getGrade(courseNo);
final int i = courseNo;
thisColumn.setCellValueFactory(cellData -> cellData.getValue().courseGradeProperty(i));
thisColumn.setCellFactory(new Callback<TableColumn<AResultRow, String>, TableCell<AResultRow, String>>() {
public TableCell<AResultRow, String> call(TableColumn<AResultRow, String> column) {
return new TableCell<AResultRow, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setText(item);
AResultRow thisRow = new AResultRow();
thisRow = getTableView().getItems().get(getTableRow().getIndex());
final Tooltip tip= new Tooltip();
tip.setText(thisRow.getCourseTootip(i));
setTooltip(tip);
tip.setStyle("-fx-background-color: pink; -fx-text-fill: black; -fx-font: normal normal 12pt \"Times New Roman\"");
}
}
};
}
});
boolean retVal = thisTable.getColumns().addAll(thisColumn);
}
// System.out.println("# of Rows in Table [" + thisSemester + "] = " + TableRows.size());
TableColumn<AResultRow, String> tcolGPA = new TableColumn<>("GPA");
tcolGPA.setEditable(false);
tcolGPA.setPrefWidth(80);
tcolGPA.setStyle("-fx-alignment: CENTER; font-weight:bold;");
tcolGPA.setCellValueFactory(cellData -> cellData.getValue().returnStringGPA());
boolean retVal = thisTable.getColumns().addAll(tcolGPA);
thisTable.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
thisTable.setItems(TableRows);
thisTable.getSelectionModel().selectedItemProperty().addListener((observableValue, oldValue, newValue) -> {
//Check whether item is selected and set value of selected item to Label
if (thisTable.getSelectionModel().getSelectedItem() == null) {
gRollNumber = null;
gStudentName = null;
} else {
gRollNumber = newValue.getStudentID();
gStudentName = newValue.getStudentName();
}
});
ScrollPane thisScrollPane = new ScrollPane();
thisScrollPane.setFitToWidth(true);
thisScrollPane.setFitToHeight(true);
thisScrollPane.setMinHeight((theDetails.getHeight() - 25));
thisScrollPane.setMaxHeight((theDetails.getHeight() - 25));
thisScrollPane.setMinWidth((theDetails.getWidth() - 25));
thisScrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS);
Tab thisTab = tabs.getTabs().get(thisSemester);
thisTab.setContent(thisScrollPane);
thisScrollPane.setContent(thisTable);
}
I am repeating the hierarchy again - please excuse.
Table view is associated with an observablelist named ATableRows which a class ATableRow.
ATableRow contains several members and one of them is an array of class ACourseResult.
I need to know the ROW number and the array index (which is actually the Table Column number for that Cell) before I can retrieve the text for the tooltip.
Thing is the code works... except for the runtime error of null pointer. I still do not understand what the CellFactory and CellValueFactories do. Sorry about that. Oracle's documents do not say what they do......
While I am at this.... I want to tell you that my TABLE is READ ONLY. Do I Have to use the Observable List ? Can't I do this by setting values directly to each cell (just a curiosity).
Thanks in advance and sorry if my questions seem dumber.
Thanks to JAMES my problem is solved.... I am enclosing the modified code for others.
for (int courseNo = 0; courseNo < numberOfCourses; courseNo++) {
String colName = getASemesterCourse(thisSemester, courseNo).getCourseID();
TableColumn<AResultRow, String> thisColumn = new TableColumn<>(colName);
thisColumn.setPrefWidth(80);
thisColumn.setStyle("-fx-alignment: CENTER; font-weight:bold;");
String str = TableRows.get(1).getGrade(courseNo);
final int i = courseNo;
thisColumn.setCellValueFactory(cellData -> cellData.getValue().courseGradeProperty(i));
thisColumn.setCellFactory(new Callback<TableColumn<AResultRow, String>, TableCell<AResultRow, String>>() {
public TableCell<AResultRow, String> call(TableColumn<AResultRow, String> column) {
return new TableCell<AResultRow, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setText(item);
AResultRow thisRow = new AResultRow();
final int k = this.getIndex(); **// These are the changes suggested by James**
thisRow = getTableView().getItems().get(k); ***// These are the changes suggested by James***
// thisRow = getTableView().getItems().get(getTableRow().getIndex()); <- this is the old code commented out.
final Tooltip tip= new Tooltip();
tip.setText(thisRow.getCourseTootip(i));
setTooltip(tip);
tip.setStyle("-fx-background-color: pink; -fx-text-fill: black; -fx-font: normal normal 12pt \"Times New Roman\"");
}
}
};
}
});
boolean retVal = thisTable.getColumns().addAll(thisColumn);
}
The quick, and wrong, answer to the question is to point out that TableCell has a getTableRow() method, that will give you the row data without having to look it up through the underlying data via the row index.
Technically, you should give the TableCell all of the information it needs to work independently when its updateItem() method is called. This would mean restructing the data model such that the table cell is given a structure with both the displayed contents of the cell, and the text to go in the tooltip. It would appear that the AResultRow data structure has two associated lists, one with whatever shows in the cell, and the other with whatever goes into the tooltip. My suggestion would be to refactor that so that it's a single list holding objects which contain both data elements. Once you do that, the rest of the table structure becomes trivial. Here's a working example:
public class SampleTable extends Application {
private BorderPane testPane;
class TestPane extends BorderPane {
public TestPane(List<DataModel> dataItems) {
TableView<DataModel> tableView = new TableView<DataModel>();
setCenter(tableView);
TableColumn<DataModel, ClassInfo> column1 = new TableColumn<DataModel, ClassInfo>("column 1");
TableColumn<DataModel, ClassInfo> column2 = new TableColumn<DataModel, ClassInfo>("column 2");
column1.setCellValueFactory(new PropertyValueFactory<DataModel, ClassInfo>("column1Data"));
column2.setCellValueFactory(new PropertyValueFactory<DataModel, ClassInfo>("column2Data"));
column1.setCellFactory(column -> new CustomTableCell());
column2.setCellFactory(column -> new CustomTableCell());
tableView.getColumns().addAll(column1, column2);
tableView.setItems(FXCollections.observableList(dataItems));
}
}
public static void main(String[] args) {
launch(args);
}
public class CustomTableCell extends TableCell<DataModel, ClassInfo> {
#Override
protected void updateItem(ClassInfo item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
if (item != null) {
setText(item.getName());
setTooltip(new Tooltip(item.getDescription()));
}
}
}
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Task Progress Tester");
List<DataModel> dataItems = new ArrayList<DataModel>();
DataModel row1Data = new DataModel();
row1Data.setColumn1Data(new ClassInfo("row1Col1Name", "This is the description for Row1, Column 1"));
row1Data.setColumn2Data(new ClassInfo("row1Col2Name", "This is the description for Row1, Column 2"));
dataItems.add(row1Data);
DataModel row2Data = new DataModel();
row2Data.setColumn1Data(new ClassInfo("row2Col1Name", "This is the description for Row2, Column 1"));
row2Data.setColumn2Data(new ClassInfo("row2Col2Name", "This is the description for Row2, Column 2"));
dataItems.add(row2Data);
testPane = new TestPane(dataItems);
primaryStage.setScene(new Scene(testPane, 300, 250));
primaryStage.show();
}
public class DataModel {
private ObjectProperty<ClassInfo> column1Data = new SimpleObjectProperty<ClassInfo>();
private ObjectProperty<ClassInfo> column2Data = new SimpleObjectProperty<ClassInfo>();
public void setColumn2Data(ClassInfo newValue) {
column2Data.set(newValue);
}
public void setColumn1Data(ClassInfo newValue) {
column1Data.set(newValue);
}
public ObjectProperty<ClassInfo> column1DataProperty() {
return column1Data;
}
}
public class ClassInfo {
private String name;
private String description;
public ClassInfo(String name, String description) {
this.setName(name);
this.setDescription(description);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
}
Short version: what is the best practice way to access maintain a history for certain messages from both an activity and from a service?
Long version:
I have an activity and a service, which both may be running or not. I want to keep a message log (history) in an object an persist it in a file and be able to e.g. delete entries.
When I have such history in the service and one in the activity I run into sync problems. So, any advice, what the best solution would be?
ideally I could use the methods from the history class in both the service and activity. Probably not possible.
I could write and read the file in each action. Probably not very efficient in the long run.
do I really need to setup a service for the history and handle all actions with it via intents?
It is a bit similiar to "proper way to access DB from both Activity and a started Service?", but with just an own class instead of a SQLite DB.
Any advice?
Conclusion: Use a ContentProvider with a SQLite-DB. Short version of the code:
package com.example.history;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
public class HistoryContentProvider extends ContentProvider {
static final String PROVIDER_NAME = "com.example.HistoryContentProvider";
static final String URL = "content://" + PROVIDER_NAME + "/history";
static final Uri CONTENT_URI = Uri.parse(URL);
static final String id = "id";
static final String normalized_number = "normalized_number";
static final String display_name = "display_name";
static final int uriCode = 1;
static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(PROVIDER_NAME, "history", uriCode);
}
#Override
public boolean onCreate() {
Context context = getContext();
DatabaseHelper dbHelper = new DatabaseHelper(context);
db = dbHelper.getWritableDatabase();
if (db != null) {
return true;
}
return false;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(TABLE_NAME);
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
#Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case uriCode:
return "vnd.android.cursor.dir/history";
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
} }
#Override
public Uri insert(Uri uri, ContentValues values) {
long rowID = db.insert(TABLE_NAME, "", values);
if (rowID > 0) {
Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
getContext().getContentResolver().notifyChange(_uri, null);
return _uri;
}
throw new SQLException("Failed to add a record into " + uri);
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
switch (uriMatcher.match(uri)) {
case uriCode:
count = db.delete(TABLE_NAME, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
return count;
}
#Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int count = 0;
switch (uriMatcher.match(uri)) {
case uriCode:
count = db.update(TABLE_NAME, values, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
return count;
}
private SQLiteDatabase db;
static final String DATABASE_NAME = "historyDb";
static final String TABLE_NAME = "history";
static final int DATABASE_VERSION = 3;
static final String CREATE_DB_TABLE = " CREATE TABLE " + TABLE_NAME
+ " (id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ normalized_number + " TEXT NOT NULL, "
+ display_name + " TEXT NOT NULL, ";
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_DB_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}
}
I have an activity and a service, which both may be running or not. I
want to keep a message log (history) in an object an persist it in a
file and be able to e.g. delete entries.
What you are describing there sounds exactly like a ContentProvider! Link to documentation.
You can use a ContentResolver instance to access data in the ContentProvider from anywhere, be it Activity or Service. The ContentProvider and ContentResolver already handle most of the work for you and basically you just need to implement how you want to save the data in the ContentProvider. The rest is already taken care of! The ContentProvider may have been designed to be used with a SQLiteDatabase - and I would recommend that you use a database - but there is nothing preventing you from saving the data in another way.
If you are not looking for DB style persistence, then maybe a Queue with File backed persistence is what you are looking for:
This maybe of use
https://github.com/square/tape/blob/master/tape/src/main/java/com/squareup/tape/QueueFile.java
Tip: Create a QueueFile singleton in your App class, and access it from your Activities or services.