Android Studio: app not saving to/reading from database - java

So my app is a QR Code scanner. Currently it will read a QR code and display it back to user. I want to get it to also save this result to a database and then proceed to read back from it. Currently it does neither of the last two and I'm struggling to figure out which is causing the issue - either saving to the database or reading back from the database.
My Database code is this:
public class Database {
private static final String DATABASE_NAME = "QRCodeScanner";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = "codes";
private OpenHelper mDbHelper;
private SQLiteDatabase mDb;
private final Context dbContext;
private static final String DATABASE_CREATE =
"CREATE TABLE " + TABLE_NAME + " (" +
"codeid INTEGER PRIMARY KEY AUTOINCREMENT, " +
"code TEXT NOT NULL);";
public Database(Context ctx) {
this.dbContext = ctx;
}
public Database open() throws SQLException {
mDbHelper = new OpenHelper(dbContext);
mDb = mDbHelper.getWritableDatabase();
return this;
}
public void close() {
mDbHelper.close();
}
public boolean createUser(String code) {
ContentValues initialValues = new ContentValues();
initialValues.put("codes", code);
return mDb.insert(TABLE_NAME, null, initialValues) > 0;
}
public ArrayList<String[]> fetchUser(String code) throws SQLException {
ArrayList<String[]> myArray = new ArrayList<String[]>();
int pointer = 0;
Cursor mCursor = mDb.query(TABLE_NAME, new String[] {"codeid", "code",
}, "code LIKE '%" + code + "%'", null,
null, null, null);
int codeNameColumn = mCursor.getColumnIndex("code");
if (mCursor != null){
if (mCursor.moveToFirst()){
do {
myArray.add(new String[3]);
myArray.get(pointer)[0] = mCursor.getString(codeNameColumn);
pointer++;
} while (mCursor.moveToNext());
} else {
myArray.add(new String[3]);
myArray.get(pointer)[0] = "NO RESULTS";
myArray.get(pointer)[1] = "";
}
}
return myArray;
}
public ArrayList<String[]> selectAll() {
ArrayList<String[]> results = new ArrayList<String[]>();
int counter = 0;
Cursor cursor = this.mDb.query(TABLE_NAME, new String[] { "codeid", "codes" }, null, null, null, null, "codeid");
if (cursor.moveToFirst()) {
do {
results.add(new String[3]);
results.get(counter)[0] = cursor.getString(0);
counter++;
} while (cursor.moveToNext());
}
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
return results;
}
private static class OpenHelper extends SQLiteOpenHelper {
OpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CREATE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}
}
And my main java code is this.
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button Scan;
private ArrayList<String[]> viewall;
private TextView QR_output;
private IntentIntegrator ScanCode;
private ListView lv;
private ArrayList Search = new ArrayList();
ArrayList<String[]> searchResult;
Database dbh;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// this caused an error on earlier APKs which made the app switch from 17 to 27
setContentView(R.layout.activity_main);
// Defines the Scan button
Scan = findViewById(R.id.Scan);
// defines the output for text
QR_output = findViewById(R.id.QR_Output);
// looks for the user clicking "Scan"
Scan.setOnClickListener(this);
ScanCode = new IntentIntegrator(this);
// Means the scan button will actually do something
Scan.setOnClickListener(this);
lv = findViewById(R.id.list);
dbh = new Database(this);
dbh.open();
}
public void displayAll(View v){
Search.clear();
viewall = dbh.selectAll();
String surname = "", forename = "";
for (int count = 0 ; count < viewall.size() ; count++) {
code = viewall.get(count)[1];
Search.add(surname + ", " + forename);
}
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
Search);
lv.setAdapter(arrayAdapter);
}
// will scan the qr code and reveal its secrets
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if (result != null) {
// if an empty QR code gets scanned it returns a message to the user
if (result.getContents() == null) {
Toast.makeText(this, "This QR code is empty.", Toast.LENGTH_LONG).show();
} else try {
// converts the data so it can be displayed
JSONObject obj = new JSONObject(result.getContents());
// this line is busted and does nothing
QR_output.setText(obj.getString("result"));
} catch (JSONException e) {
e.printStackTrace();
String codes = result.getContents();
boolean success = false;
success = dbh.createUser(codes);
// outputs the data to a toast
Toast.makeText(this, result.getContents(), Toast.LENGTH_LONG).show();
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
#Override
public void onClick(View view) {
// causes the magic to happen (It initiates the scan)
ScanCode.initiateScan();
}
}

Your issue could well be with the line initialValues.put("codes", code); as according to your table definition there is no column called codes, rather the column name appears to be code
As such using initialValues.put("code", code); may well resolve the issue.
Addititional
It is strongly recommended that you define and subsequently use constants throughout your code for all named
items (tables, columns, views trigger etc) and thus the value will always be identical.
e.g.
private static final String DATABASE_NAME = "QRCodeScanner";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = "codes";
public static final String COLUMN_CODEID = "codeid"; //<<<<<<<<< example note making public allows the variable to be used elsewhere
public static final String COLUMN_CODE = "code"; //<<<<<<<<<< another example
private OpenHelper mDbHelper;
private SQLiteDatabase mDb;
private final Context dbContext;
private static final String DATABASE_CREATE =
"CREATE TABLE " + TABLE_NAME + " (" +
COLUMN_CODEID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + //<<<<<<<<<<
COLUMN_CODE + " TEXT NOT NULL);"; //<<<<<<<<<<
........ other code omitted for brevity
public boolean createUser(String code) {
ContentValues initialValues = new ContentValues();
initialValues.put(COLUMN_CODE, code); //<<<<<<<<<< CONSTANT USED
return mDb.insert(TABLE_NAME, null, initialValues) > 0;
}
You would also likely encounter fewer issues by not using hard coded column offsets when extracting data from Cursor by rather using the Cursor getColumnIndex method to provide the offset.
e.g. instead of :-
results.get(counter)[0] = cursor.getString(0);
it would be better to use :-
results.get(counter)[0] = cursor.getString(cursor.getColumnIndex(COLUMN_CODEID));

Related

Check if data was written in SQLite Database

I want to save the Score from a Quiz in a SQLite Database and change an image in another activity if the Score is 5. There is no error shown, but even if I score 5 the image won't change... How can I log the content of my database to check if the score was added or how can I find the mistake?
DB Helper:
public class DbHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 7;
private static final String DATABASE_NAME = "CE";
public static final String SCORE_TABLE = "score";
public static final String COLUMN_ID = "ID";
public static final String COLUMN_SCORE = "SCORE";
public static final String COLUMN_MARKERID = "MARKERID";
private SQLiteDatabase dbase;
public DbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
dbase= db;
String create_query = "CREATE TABLE IF NOT EXITS " + SCORE_TABLE + " ( "
+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COLUMN_SCORE + " INTEGER, "
+ COLUMN_MARKERID + " TEXT) ";
db.execSQL(create_query);
}
public void addScore (DbHelper dbh, Integer score, String markerID) {
dbase = dbh.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(COLUMN_SCORE, score);
cv.put(COLUMN_MARKERID, markerID);
dbase.insert(SCORE_TABLE, null, cv);
}
public Cursor getScore(DbHelper dbh) {
dbase = dbh.getReadableDatabase();
String columns[] = {COLUMN_SCORE, COLUMN_MARKERID};
Cursor cursor = dbase.query(SCORE_TABLE, columns, null, null, null, null, null);
return cursor;
}
Write the Score into the Database after completing the Quiz:
public class ResultActivity extends Activity {
String markerID;
int score;
TextView t=(TextView)findViewById(R.id.textResult);
Button saveButton = (Button) findViewById(R.id.saveButton);
Context context = this;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.result_layout);
Bundle b = getIntent().getExtras();
score = b.getInt("score");
markerID = b.getString("markerID");
}
saveButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DbHelper dbh = new DbHelper(context);
dbh.addScore(dbh,score,markerID);
Intent intent = new Intent(ResultActivity.this, Discover.class);
intent.putExtra("MarkerID", markerID);
startActivity(intent);
}
});
}
Discover class -> Check if score is 5 and change image if:
DbHelper dbh = new DbHelper(context);
Cursor cursor = dbh.getScore(dbh);
cursor.moveToFirst();
if (cursor.moveToFirst()) {
do {
if (Integer.parseInt(cursor.getString(0))== 5 && InfoUeberschrift.toString().equals(cursor.getString(1))){
ImageDone.setImageResource(R.drawable.markerdone);
}
}
while(cursor.moveToNext());
}
cursor.close();
}
The SQLiteDatabase insert function returns a long value, so if an error has occurred it returns -1.
'the row ID of the newly inserted row, or -1 if an error occurred'
http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html
This can be used to see if the insert is happening correctly.
Or you can wrap in try and catch and print message like so
try {
//code
} catch(SQLiteException ex) {
Log.v("Insert into database exception caught", ex.getMessage());
return -1;
}
}
When I have issues using Java and SQLite i normally do it directly with the SQLite desktop version using Shell, as I find it easier to test out table design.
Hope this helps

SQlite delete function is not deleting entries

Im having trouble getting individual items to delete in a database my application is using. I know the method gets called, but nothing in my list is ever removed. Im not getting any errors which is making it tough to track down. Assistance would be awesome.
public class MainActivity extends Activity{
//Global Variables
ListView lv;
Intent addM, viewM;
public DBAdapter movieDatabase;
String tempTitle, tempYear;
int request_Code = 1;
int request_code2 = 2;
SimpleCursorAdapter dataAdapter;
Cursor cursor;
Button addButton;
long testID;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//creates the database
movieDatabase = new DBAdapter(this);
movieDatabase.open();
//movieDatabase.deleteAllMovies();
//creates the intents to start the sub activities
addM = new Intent(this, AddMovie.class);
viewM = new Intent(this, MovieView.class);
}
//handles the return of the activity addMovie
public void onActivityResult(int requestCode, int resultCode,
Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_OK)
{
switch(requestCode)
{
case 1:
dbAddMovie(data.getStringExtra("title"),
data.getStringExtra("year"));
break;
case 2:
testID = data.getLongExtra("rowid", -1);
dMovie(testID);
break;
}
}
}
//adds item to the movie list
public void dbAddMovie(String mT, String mY)
{
movieDatabase.open();
movieDatabase.insertMovie(mT, mY);
Toast.makeText(this, "Movie: " + mT + " added to database",
Toast.LENGTH_SHORT).show();
}
//deletes an entry into the database
public void dMovie(long rowid)
{
//Toast.makeText(this, "Deleting: " + rowid,
Toast.LENGTH_SHORT).show();
movieDatabase.deleteMovie(rowid);
movieDatabase.getAllMovies();
}
//displays the database as a list
public void displayListView()
{
addButton = (Button) findViewById(R.id.add);
addButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivityForResult(addM, 1);
}
});
cursor = movieDatabase.getAllMovies();
//columns to use
String[] columns = new String[]
{
movieDatabase.KEY_TITLE,
};
//xml data to bind the data to
int[] to = new int[]
{
R.id.column2,
};
//adapter to display the database as a list
dataAdapter = new SimpleCursorAdapter(this,
R.layout.complexrow, cursor, columns, to, 0);
//gets the List view resource
lv = (ListView) findViewById(R.id.movielist);
//sets the list view to use the adapter
lv.setAdapter(dataAdapter);
//handles the list click events
lv.setOnItemClickListener(new
AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View
v, int position,
long id) {
Cursor cursor = (Cursor)
parent.getItemAtPosition(position);
Bundle mDet = new Bundle();
mDet.putString("title",
cursor.getString(cursor.getColumnIndex(movieDatabase.KEY_TITLE)));
mDet.putString("year",
cursor.getString(cursor.getColumnIndex(movieDatabase.KEY_YEAR)));
mDet.putInt("rId", position);
viewM.putExtras(mDet);
startActivityForResult(viewM, 2);
}
});
//dataAdapter.notifyDataSetChanged();
}
public void onResume()
{
super.onResume();
displayListView();
}
}
and my coresponding dbadapter class
public class DBAdapter {
public static final String KEY_ROWID = "_id";
public static final String KEY_TITLE = "title";
public static final String KEY_YEAR = "year";
private static final String TAG = "DBAdapter";
private static final String DATABASE_NAME = "MovieListDB";
private static final String DATABASE_TABLE = "MoviesTable";
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_CREATE = "create table MoviesTable (_id
integer primary key autoincrement, " +
"title text not null, year not null);";
private final Context context;
private DatabaseHelper DBHelper;
private SQLiteDatabase db;
public DBAdapter(Context ctx)
{
this.context = ctx;
DBHelper = new DatabaseHelper(context);
}
private static class DatabaseHelper extends SQLiteOpenHelper
{
DatabaseHelper(Context context)
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
try{
db.execSQL(DATABASE_CREATE);
} catch (SQLException e)
{
e.printStackTrace();
}
}
#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 MoviesTable");
onCreate(db);
}
}
public DBAdapter open() throws SQLException
{
db = DBHelper.getWritableDatabase();
return this;
}
public void close()
{
DBHelper.close();
}
public long insertMovie(String title, String year)
{
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_TITLE, title);
initialValues.put(KEY_YEAR, year);
return db.insert(DATABASE_TABLE, null, initialValues);
}
public boolean deleteMovie(long rowID)
{
return db.delete(DATABASE_TABLE, KEY_ROWID + "='" + rowID+"'", null ) >-1;
}
public Cursor getAllMovies()
{
return db.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_TITLE,
KEY_YEAR}, null, null, null, null, null);
}
public Cursor getMovie(long rowID) throws SQLException
{
Cursor mCursor =
db.query(true, DATABASE_TABLE, new String[] {KEY_ROWID,
KEY_TITLE, KEY_YEAR}, KEY_ROWID + "=" + rowID, null, null, null, null, null);
if(mCursor != null)
{
mCursor.moveToFirst();
}
return mCursor;
}
public boolean updateContact(long rowID, String title, String year)
{
ContentValues args = new ContentValues();
args.put(KEY_TITLE, title);
args.put(KEY_YEAR, year);
return db.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowID, null) > 0;
}
public void deleteAllMovies() {
int doneDelete = 0;
doneDelete = db.delete(DATABASE_TABLE, null, null);
}
}
You're using the position returned from your listview as the row id in your database. This won't necessarily match up with your autoincremented "_id" in your database. position is just what position in the list it is.
You might want to think about using movieDatabase.KEY_ROWID as the key for your intents. Right now I see a mix of "rowid", "rId", "_id", and KEY_ROWID. It would simplify thing to just use the same key everywhere when referring to the same thing.
It looks like you continuously add bundles to the viewM intent. Is that true? If that's not your intent, you should either create a new intent for each click, or remove the previous bundles first.
I'm assuming KEY_ROWID is actually the name of the column? Try the following:
public boolean deleteMovie(long rowID)
{
return db.delete(DATABASE_TABLE, KEY_ROWID + "=?", new String[] { String.valueOf(rowID) }) >-1;
}

SimpleCursorAdapter does not load external sqlite database: "_id" error

The error says: column _id does not exists but the column is in the database (set as primary key) and this one is located in the external SD folder. I'm trying to return the values contained in the database on the initial load of the activity but it seems like the cursor is not returning anything.
public class ComponentsDbAdapter {
public static final String COLUMN_ID = "_id";
public static final String COLUMN_SUBSTRUCTURE = "substructure";
public static final String COLUMN_TYPE = "type";
public static final String COLUMN_ORDERNUM = "ordernum";
public static final String COLUMN_INSTALLATION = "installation";
private static final String TAG = "ComponentsDbAdapter";
private DatabaseHelper mDbHelper;
private SQLiteDatabase mDb;
private static final String DATABASE_PATH = Environment.getExternalStorageDirectory().getAbsoluteFile()+ "/DATABASE_BACKUP/IMPORTED/";
private static final String DATABASE_NAME = "android.db";
private static final String TABLE_NAME = "TAB_WORKSCPE";
private static final int DATABASE_VERSION = 1;
private final Context mCtx;
public ComponentsDbAdapter open() throws SQLException {
mDbHelper = new DatabaseHelper(mCtx);
mDb = mDbHelper.getWritableDatabase();
return this;
}
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_PATH+DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.query(TABLE_NAME, new String[] {COLUMN_ID, COLUMN_SUBSTRUCTURE, COLUMN_TYPE, COLUMN_ORDERNUM, COLUMN_INSTALLATION}, null, null, null, null, null);
}
#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 " + TABLE_NAME);
onCreate(db);
}
}
public ComponentsDbAdapter(Context ctx) {
this.mCtx = ctx;
}
public void close() {
if (mDbHelper != null) {
mDbHelper.close();
}
}
public Cursor fetchComponentsByName(String inputText) throws SQLException {
Log.w(TAG, inputText);
Cursor mCursor = null;
if (inputText == null || inputText.length () == 0) {
mCursor = mDb.query(TABLE_NAME, new String[] {COLUMN_ID, COLUMN_SUBSTRUCTURE, COLUMN_TYPE, COLUMN_ORDERNUM, COLUMN_INSTALLATION}, null, null, null, null, null);
} else {
mCursor = mDb.query(true, TABLE_NAME, new String[] {COLUMN_ID, COLUMN_SUBSTRUCTURE, COLUMN_TYPE, COLUMN_ORDERNUM, COLUMN_INSTALLATION}, COLUMN_TYPE + " like '%" + inputText + "%'", null, null, null, null, null);
}
if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}
public Cursor fetchAllComponents() {
Cursor mCursor = mDb.query(TABLE_NAME, new String[] {COLUMN_ID, COLUMN_SUBSTRUCTURE, COLUMN_TYPE, COLUMN_ORDERNUM, COLUMN_INSTALLATION}, null, null, null, null, null);
if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}
}
public class AndroidListViewCursorAdaptorActivity extends Activity {
private ComponentsDbAdapter dbHelper;
private SimpleCursorAdapter dataAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
dbHelper = new ComponentsDbAdapter(this);
dbHelper.open();
//Generate ListView from SQLite Database
displayListView();
}
private void displayListView() {
Cursor cursor = dbHelper.fetchAllComponents();
// The desired columns to be bound
String[] columns = new String[] {
ComponentsDbAdapter.COLUMN_SUBSTRUCTURE,
ComponentsDbAdapter.COLUMN_TYPE,
ComponentsDbAdapter.COLUMN_ORDERNUM,
ComponentsDbAdapter.COLUMN_INSTALLATION
};
// the XML defined views which the data will be bound to
int[] to = new int[] {
R.id.inst,
R.id.subdt,
R.id.type,
R.id.ordernum,
};
// create the adapter using the cursor pointing to the desired data
//as well as the layout information
dataAdapter = new SimpleCursorAdapter(
this,
R.layout.country_info,
cursor,
columns,
to,
0);
ListView listView = (ListView) findViewById(R.id.listView1);
// Assign adapter to ListView
listView.setAdapter(dataAdapter);
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> listView, View view,
int position, long id) {
// Get the cursor, positioned to the corresponding row in the result set
Cursor cursor = (Cursor) listView.getItemAtPosition(position);
// Get the state's capital from this row in the database.
String compSubdt = cursor.getString(cursor.getColumnIndexOrThrow("subdt"));
Toast.makeText(getApplicationContext(), compSubdt, Toast.LENGTH_SHORT).show();
}
});
EditText myFilter = (EditText) findViewById(R.id.myFilter);
myFilter.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
}
public void beforeTextChanged(CharSequence s, int start,int count, int after) {
}
public void onTextChanged(CharSequence s, int start,int before, int count) {
dataAdapter.getFilter().filter(s.toString());
}
});
dataAdapter.setFilterQueryProvider(new FilterQueryProvider() {
public Cursor runQuery(CharSequence constraint) {
return dbHelper.fetchComponentsByName(constraint.toString());
}
});
}
}
It doesn't appear from your code that you've created the table yet, so no columns will be found.
You do this within the onCreate method by creating a query to create the table. In your code you appear to be doing a select rather than create.
private static final String TABLE_CREATE = "create table "
+ TABLE_NAME
+ "("
+ COLUMN_ID + " integer primary key autoincrement, "
+ COLUMN_TYPE + " text not null default '', "
+ COLUMN_ORDERNUM + " integer not null default 0, "
+ COLUMN_INSTALLATION + " integer not null default 0, "
+ COLUMN_SUBSTRUCTURE + " text not null default ''"
+ ");";
#Override
public void onCreate(SQLiteDatabase database) {
database.execSQL(TABLE_CREATE);
}
To store this on the external storage, you'll need to override getDatabasePath(...). A similar solution is here https://stackoverflow.com/a/8385537/935779
#Override
public File getDatabasePath(String name) {
// reference where you would like the file to be here.
File result = new File(getExternalFilesDir(null), name);
return result;
}
I believe you'll want to override this with your Application class since it's a member of ContextWrapper.
The method getDatabaseFile(...) is used inside of openOrCreateDatabase(...) to determine the location.
Alternatively you could just override openOrCreateDatabase(...) and set the file location there.
I don't think you can change or even specify the location of the database, only the name.
Leave off the path and don't try to put it in External Storage - let Android determine the path.
Ok, this took me almost week and a lot of stress but here is the solution. I started to go through a lot of tutorials and got it working in this one:
http://www.mysamplecode.com/2012/11/android-database-content-provider.html
I extracted the database from the virtual device and manually added more data. Then copied the database to the desired folder on my device folder (Its just to make sure the database consistency/columns are exactly the same). Then changed MyDatabaseHelper class as follows:
public class MyDatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_PATH = Environment.getExternalStorageDirectory().getAbsoluteFile()+ "/MYFOLDER/";
private static final String DATABASE_NAME = "TheWorld.db";
private static final int DATABASE_VERSION = 1;
MyDatabaseHelper(Context context) {
super(context, DATABASE_PATH+DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
CountriesDb.onCreate(db);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
CountriesDb.onUpgrade(db, oldVersion, newVersion);
}
}
Don't forget to add permissions to your manifest:
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Done!
If you read through the posts above the answer is based on Kirks advice so reading his recommended link helps. I still have more tests to do just in case my database structure was wrong before.

Can write files to server in the emulator but not the device?

So I've run into a peculiar problem. I am trying to write a data file to the device. I developed the app in eclipse under 2.2, my device is 2.3.3, so I made the emulator run at 2.3.3, it writes files fine. Why does this not work on the device? It also is coded to copy the database file on the device to a php server. On the server the file is essentially empty, i pulled the db file from the device as well and it's empty. The only time it works is on the emulator, i get a legit file on the server and when i pull the database it's got data in it. I'm so lost. If you want to see some code then ask, I would have posted some but with 40+ classes I really don't know where to begin.
Thanks in advance.
Here is code relating to the database creation
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class DbHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "database";
private static final int DATABASE_VERSION = 1;
// Database creation sql statement
private static final String DATABASE_CREATE = "create table database (_id integer primary key autoincrement," +
"name text not null);";
public DbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
// Method is called during creation of the database
#Override
public void onCreate(SQLiteDatabase database) {
database.execSQL(DATABASE_CREATE);
}
// Method is called during an upgrade of the database, e.g. if you increase
// the database version
#Override
public void onUpgrade(SQLiteDatabase database, int oldVersion,
int newVersion) {
Log.w(DbHelper.class.getName(),
"Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
database.execSQL("DROP TABLE IF EXISTS database");
onCreate(database);
}
}
here is the adapter..
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
public class DbAdapter {
// Database fields
public static final String KEY_ROWID = "_id";
public static final String KEY_NAME = "name";
static final String DATABASE_TABLE = "database";
private Context context;
private SQLiteDatabase database;
private DbHelper dbHelper;
public DbAdapter(Context ctx) {
context = ctx;
}
public SQLiteDatabase openToRead() throws SQLException {
dbHelper = new DbHelper(context);
database = dbHelper.getReadableDatabase();
return database;
}
public SQLiteDatabase open() throws SQLException {
dbHelper = new DbHelper(context);
database = dbHelper.getWritableDatabase();
return database;
}
public void close() {
dbHelper.close();
}
//
/**
* Create a new todo If the todo is successfully created return the new
* rowId for that note, otherwise return a -1 to indicate failure.
*/
public long createRow(String name) {
ContentValues initialValues = createContentValues(name);
return database.insert(DATABASE_TABLE, null, initialValues);
}
/**
* Update the todo
*/
public boolean updateRows(long rowId,
String name) {
ContentValues updateValues = createContentValues(
name);
return database.update(DATABASE_TABLE, updateValues, KEY_ROWID + "="
+ rowId, null) > 0;
}
/**
* Deletes todo
*/
public boolean deleteRow(long rowId) {
return database.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0;
}
/**
* Return a Cursor over the list of all todo in the database
*
* #return Cursor over all notes
*/
public Cursor fetchAllRows() {
return database.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_NAME}, null, null, null,
null, null);
}
/**
* Return a Cursor positioned at the defined todo
*/
public Cursor fetchRow(long rowId) throws SQLException {
Cursor mCursor = database.query(true, DATABASE_TABLE, new String[] {
KEY_ROWID ,KEY_NAME},
KEY_ROWID + "=" + rowId, null, null, null, null, null);
if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}
/**fetches keyword**/
public Cursor fetchKeyword(String keyword, String column, String[] columns) throws SQLException {
Cursor mCursor = database.query(DATABASE_TABLE, columns, column + "='" + keyword + "'",
null, null, null, null);
if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}
private ContentValues createContentValues(String name){
ContentValues values = new ContentValues();
values.put(KEY_NAME, name);
return values;
}
//returns (an) entire column(s), all rows
public Cursor fetchColumns(String[] colnames) {
Cursor mCursor = database.query(DATABASE_TABLE, colnames, null,
null, null, null, null);
if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}
}
Ok, I've Isolated it to these three classes.. below is the class that creates the entry and uploads it to the server. as far as i can tell the database isn't being created the right way.. as in there are no tables in the file
public class CreateName extends Activity{
//variables
private DbAdapter mDbHelper;
EditText textField;
TextView txtEnter2;
TextView txtEnter;
Button btnSubmit;
Context context;
SharedPreferences prefs;
SharedPreferences.Editor spe;
SQLiteDatabase db;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.submit);
//constructors
context = getApplicationContext();
prefs = PreferenceManager.getDefaultSharedPreferences(context);
spe = prefs.edit();
init();
listen();
}
public void checker() {
Intent i = new Intent(this, MoveForward.class);
startActivity(i);
}
private void listen() {
btnSubmit.setOnClickListener(new Button.OnClickListener()
{
public void onClick (View v)
{
mDbHelper = new DbAdapter(context);
String words = textField.getText().toString();
Log.v(words, words);
mDbHelper.open();
mDbHelper.createRow(words);
mDbHelper.close();
spe.putString("name", words);
spe.commit();
PHPBuddy buddy = new PHPBuddy();
try {
buddy.uploadFile("database");
} catch (Exception e) {
// ask the user to retry
e.printStackTrace();
}
checker();
}
}
);
}
private void init() {
textField = (EditText)findViewById(R.id.edtTxt);
txtEnter2 = (TextView)findViewById(R.id.txtEnter2);
txtEnter = (TextView)findViewById(R.id.txtEnter);
btnSubmit =(Button)findViewById(R.id.btnSubmit);
txtEnter.setText("Enter your proper name");
txtEnter2.setText("ex: John Smith");
}
}
Maybe there is a cursor or db i'm forgetting to close?
Here is more code..
public class SBMain extends Activity {
Button btnSpinner;
String[] items;
String text;
Spinner s;
Intent i, j;
int activity;
SharedPreferences prefs;
SharedPreferences.Editor spe;
SQLiteDatabase db;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.spinnerscreen);
prefs = PreferenceManager.getDefaultSharedPreferences(this);
spe = prefs.edit();
init();
fillSpinner();
btnSpinner.setOnClickListener(new Button.OnClickListener()
{
public void onClick (View v)
{
Cursor cc = (Cursor)(s.getSelectedItem());
if (cc != null) {
text = cc.getString(cc.getColumnIndex("name"));
}
checker();
}
});
}
public void checker() {
if (text .equals("Create Name")){
i = new Intent(this, GetName.class);
spe.putString("name", text);
spe.commit();
startActivity(i);
}else{
spe.putString("name", text);
spe.commit();
i = new Intent(this, MoveForward.class);
startActivity(i);
}
}
private void fillSpinner(){
DbAdapter mDbHelper = new DbAdapter(this);
mDbHelper.open();
Cursor c = mDbHelper.fetchColumns(new String[] {"_id","name"});;
if ( ! c.moveToFirst() ){
c.close();
mDbHelper.createRow("Create Name");
mDbHelper.close();
c = mDbHelper.fetchColumns(new String[] {"_id","name"});
}else{
mDbHelper.close();
}
// create an array to specify which fields we want to display
String[] from = new String[]{"name"};
// create an array of the display item we want to bind our data to
int[] to = new int[]{android.R.id.text1};
// create simple cursor adapter
SimpleCursorAdapter adapter =
new SimpleCursorAdapter(this, android.R.layout.simple_spinner_item, c, from, to );
adapter.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item );
// get reference to our spinner
s.setAdapter(adapter);
}
private void init() {
btnSpinner = (Button)findViewById(R.id.btnSpinner);
s = (Spinner) findViewById( R.id.spinner1 );
}
}
Here is the splash activity that downloads the file
public class Splash extends Activity{
String file_url = "http://ipaddress/xampp/uploads/";
Context context = this;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
Thread splashThread = new Thread() {
#Override
public void run() {
float percent = 0;
try {
int waited = 0;
int time = 1000;
while (waited < time) {
sleep(100);
waited += 100;
String perc = Integer.toString(waited / time);
}
} catch (InterruptedException e) {
// do nothing
} finally {
//if this is the apps first time running, get a list of names.
if(isFirstRun()){
PHPBuddy buddy = new PHPBuddy();
try {
buddy.downloadFile("database");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//ToDo add spared prefs editor to set isfirstrun to true
Intent i = new Intent();
i.setClassName("com.project",
"com.project.SBMain");
startActivity(i);
}else{
//make registered user page
Intent i = new Intent();
//ToDo add spared prefs editor to set isfrstrun to false
//ToDo add intent for true
}
finish();
}
}
};
splashThread.start();
}
public boolean isFirstRun(){
String rb = "isfirstrun";
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor spe;
spe = prefs.edit();
//spe.putBoolean("isfirstrun", true);
//boolean test = prefs.getBoolean(rb, true);
return true;//prefs.getBoolean(rb, true);
}
}
here is the php
<?php
$myFile = "requestslog.txt";
$fh = fopen($myFile, 'a') or die("can't open file");
fwrite($fh, "\n\n---------------------------------------------------------------\n");
foreach($_SERVER as $h=>$v)
if(ereg('HTTP_(.+)',$h,$hp))
fwrite($fh, "$h = $v\n");
fwrite($fh, "\r\n");
fwrite($fh, file_get_contents('php://input'));
fclose($fh);
echo "<html><head /><body><iframe src=\"$myFile\" style=\"height:100%; width:100%;\"> </iframe></body></html>"
?>
<?php
if (is_uploaded_file($_FILES['userfile']['tmp_name'])) {
echo "File ". $_FILES['userfile']['name'] ." uploaded successfully.\n";
move_uploaded_file ($_FILES['userfile'] ['tmp_name'], $_FILES['userfile'] ['name']);
} else {
echo "Possible file upload attack: ";
echo "filename '". $_FILES['userfile']['tmp_name'] . "'.";
print_r($_FILES);
}
?>
my first guess is that you are trying to write the database somewhere where you don't have permission.
i would recommend using the method File getDir (String name, int mode) of your Activity to get a folder inside your app where you can read and write.
I think I figured it out.
First I set up a class that would copy the database to the SD card right after it was created; from there, I examined the file and saw that it was indeed intact.
Then, I moved the copy call to the place right before the database gets sent to the server. It didn't look fine in "SQLite Database Browser," so I opened it up in Firefox's SQLite manager and voila, all the data was there. So, with Firefox I go and look at the file that was uploaded to the server. What looked empty in the SQLite Database Browser has data in it!

close() was not explicity called on database

I need to know where to call the db.close() in my code. I've added it on onCreate() method, but when I need to use some methods it says database not open, and then I've removed from onCreate() and it says close() was not explicity called. so where should I close, could it be inside each method of the class??
here is the code:
public class HoursPerDayDataHelper {
private static final String DATABASE_NAME = "database.db";
private static final int DATABASE_VERSION = 1;
protected static final String TABLE_NAME = "table";
protected String TAG = "HoursPerDayDataHelper";
private Context context;
private SQLiteDatabase db;
OpenHelper openHelper = null;
public HoursPerDayDataHelper(Context context) {
this.context = context;
openHelper = new OpenHelper(this.context);
this.db = openHelper.getWritableDatabase();
openHelper.onCreate(db);
}
public void close() {
if (openHelper != null) {
openHelper.close();
}
}
public void deleteAll() {
this.db.delete(TABLE_NAME, null, null);
}
public String selectDuration(String date) {
String duration = "";
Integer value = 0;
String returnment = "";
Log.i(TAG, "date do select: " + date);
Cursor cursor = this.db.query(TABLE_NAME, new String[] { "duration" },
"date = ? ", new String[]{ date }, null, null, null);
Log.i(TAG, "cursor string " + cursor);
if (cursor.moveToFirst()) {
do {
Log.i(TAG, "dentro do if cursor");
duration = cursor.getString(0);
value += Integer.parseInt(duration);
} while (cursor.moveToNext());
returnment = Integer.toString(value);
}else{
Log.i(TAG, "bla bla bla");
}
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
return returnment;
}
public ArrayList<String[]> selectTopContacts() {
ArrayList<String[]> list1 = new ArrayList<String[]>();
Cursor cursor = this.db.query(TABLE_NAME, null, null, null, null, null,
"duration desc");
if (cursor.moveToFirst()) {
do {
if (cursor.getString(2) != "") {
String[] data = new String[4];
data[0] = cursor.getString(2);
data[1] = cursor.getString(4);
data[2] = cursor.getString(5);
data[3] = cursor.getString(7);
list1.add(data);
} else {
String[] data = new String[3];
data[1] = cursor.getString(4);
data[2] = cursor.getString(5);
data[3] = cursor.getString(7);
list1.add(data);
}
} while (cursor.moveToNext());
}
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
return list1;
}
public static class OpenHelper extends SQLiteOpenHelper {
OpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS "
+ TABLE_NAME
+ "(id INTEGER PRIMARY KEY AUTOINCREMENT, duration TIME, date DATE, current_time TIME)");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w("HoursPerDay Database",
"Upgrading database, this will drop tables and recreate.");
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}
}
And this is my Activity:
public class HoursPerDay extends Activity{
private String LOG_TAG = "HoursPerDay";
private TextView mDateDisplay;
public String date;
private int mYear;
private int mMonth;
private int mDay;
private int newDay;
private String hpdData;
private HoursPerDayDataHelper hpd;
OpenHelper openHelper = new OpenHelper(HoursPerDay.this);
static final int DATE_DIALOG_ID = 0;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.hours_per_day);
hpd = new HoursPerDayDataHelper(this);
// capture our View elements
mDateDisplay = (TextView) findViewById(R.id.dateDisplay);
// get the current date
final Calendar c = Calendar.getInstance();
mYear = c.get(Calendar.YEAR);
mMonth = c.get(Calendar.MONTH);
mDay = c.get(Calendar.DAY_OF_MONTH);
// display the current date (this method is below)
}
#Override
protected void onDestroy() {
super.onDestroy();
if (openHelper != null) {
openHelper.close();
}
if (hpd != null) {
hpd.close();
}
}
// the callback received when the user "sets" the date in the dialog
private DatePickerDialog.OnDateSetListener mDateSetListener = new DatePickerDialog.OnDateSetListener() {
public void onDateSet(DatePicker view, int year, int monthOfYear,
int dayOfMonth) {
mYear = year;
mMonth = monthOfYear;
mDay = dayOfMonth;
setBasicContent();
hpd.close();
}
};
protected Dialog onCreateDialog(int id) {
switch (id) {
case DATE_DIALOG_ID:
return new DatePickerDialog(this,
mDateSetListener,
mYear, mMonth, mDay);
}
return null;
}
#Override
public boolean onCreateOptionsMenu(Menu menu){
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.layout.hoursperdaymenu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item){
switch(item.getItemId()){
case R.id.filter_by_day:
showDialog(DATE_DIALOG_ID);
return true;
case R.id.filter_by_user:
showDialog(DATE_DIALOG_ID);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public void setBasicContent() {
date = (mMonth + 1) + "/" + newDay + "/" + mYear;
hpdData = this.hpd.selectDuration(date);
mDateDisplay.setText(hpdData);
hpd.close();
}
}
I think there are 2 ways:
in onPause-method and check there isFinishing if yes -> close. Problem: if your app gets killed by app-killer, db remains open.
You open and close the DB each time (methods) you read/write.
EDIT:
Ok, I see why it could be caused. I think you misunderstood the usage of the SQLiteOpenHelper. You never have to call the onCreate-method.
Defently the better way is to make a DBHelper class and use it in a separate calls, lets say SQLDataHandler.
Your activity look good. I changed a few things, look if it helps. I'll mark them:
That's all what should be in the Helper class:
public static class OpenHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "database.db";
private static final int DATABASE_VERSION = 1;
protected static final String TABLE_NAME = "table";
protected String TAG = "HoursPerDayDataHelper";
Just leave it CREATE TABLE it gets only created/called if there isn't one existing.
I have seen errors occurring if the String is passed directly
#Override
public void onCreate(SQLiteDatabase db) {
String query = "CREATE TABLE "
+ TABLE_NAME
+ "(id INTEGER PRIMARY KEY AUTOINCREMENT, duration TIME, date DATE, current_time TIME)";
db.execSQL(query);
}
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
OpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
To use it:
Just call in your DataHandler class :
OpenHelper helper = new OpenHelper(ctx);
// SQLiteDatabase db = helper.getReadableDatabase();
SQLiteDatabase db = helper.getWritableDatabase();
All other stuff, like deleting, adding and so on, should be done in a "DataHandler" class.
Just use the same two methods there to get your DB. At the end, when you are finished, you call just in you DataHandler class db.close().
Like this the activity itself never uses de DB directly. Better practice I think ;)
I hope it helps. For any other questions, just ask :)
EDIT2:
First, in general it should work with a inner class.
BUT: In case you want to add another table from another class it won't work anymore. Thats why it's the better way to put it in a separate class from beginning. It's even reusable (with some smal adjustments).
Put the code I posted in your class OpenHelper. Nothing more.
Then, put the data manipulation stuff in a class called something like: DataHandlerDB.
Code example:
package ...;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
public class DataHandlerDB {
public static void persistAll(Context ctx, List<Module> moduleList) {
DatabaseHelper helper = new DatabaseHelper(ctx);
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
for (Module m : moduleList) {
values.put("_id", m.get_id());
values.put("name", m.getModule());
db.insert("module", null, values);
}
db.close();
}
public static List<Module> findAll(Context ctx) {
List<Module> result = new ArrayList<Module>();
DatabaseHelper helper = new DatabaseHelper(ctx);
SQLiteDatabase db = helper.getReadableDatabase();
Cursor c = db.query(ModuleDB.TABLE_NAME, new String[] { ModuleDB.ID,
ModuleDB.MODULE}, null, null, null, null, null);
while (c.moveToNext()) {
Module m = new Module(c.getInt(0), c.getString(1));
result.add(m);
}
c.close();
db.close();
return result;
}
// Update Database entry
public static void update(Context ctx, Module m) {
DatabaseHelper helper = new DatabaseHelper(ctx);
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("_id", m.get_id());
values.put("name", m.getModule());
db.update("module", values, null, null);
db.close();
}
public static void delete(Context ctx, Module m) {
DatabaseHelper helper = new DatabaseHelper(ctx);
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("_id", m.get_id());
values.put("name", m.getModule());
db.delete("module","_id = m.get_id()", null);
db.close();
}
public static void createDB(Context ctx) {
DatabaseHelper helper = new DatabaseHelper(ctx);
SQLiteDatabase db = helper.getWritableDatabase();
db.close();
}
}
In order to be more efficient the methods are static, you won't need to create objects.
Use it like this: In your activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get the a writable DB, in case it's not existing it gets created.
DataHandlerDB.createDB(this);
// get stuff out of DB
moduleList = DataHandlerDB.findAll(this);
adapter = new ArrayAdapter<Module>(this,
android.R.layout.simple_list_item_1, moduleList);
setListAdapter(adapter);
}
if you open in onCreate, then close in onDestroy

Categories