I'm trying to create a app of baking sell expenses, where I can input the values of the ingredients used on the baking process individually, and then input the values of each product using the values of the ingredients in each recipe, so I can see how much I can sell then to have a good profit.
I have a database and methods working on my first table (Save, Delete, Update, Search...), but I can't make my second table (TABLE_RECEITA) uses the data that already exist from my first table (TABLE_PRODUTO), to complement my recipes.
I want to feed my second table on it's columns with the data from my first table columns, only changing the data of quantity, and value by dividing according to the amount used.
I've tried to used foreign key and spinner but was not successful, but there's a high possibility that I did something wrong.
My Database:
package com.myapplication.umdocededaisy;
import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MyDatabase extends SQLiteOpenHelper {
List<MateriaPrima> listaProduto = new ArrayList<>();
List<Receita> listaReceita = new ArrayList<>();
private final Context context;
private static final String DATABASE_NAME = "BancoDoceDaisy.db";
private static final int DATABASE_VERSION = 9;
//Estruturas das Tabelas do banco de dados:
//Tabela dos produtos - materia prima **(FIRST TABLE)**:
private static final String TABLE_PRODUTO = "materia_prima";
private static final String COLUMN_CODIGO = "codigo";
private static final String COLUMN_PRODUTO = "produto";
private static final String COLUMN_VALOR = "valor";
private static final String COLUMN_QTD = "quantidade";
private static final String COLUMN_TIPO = "tipo";
//------------------------------------------------------
//Tabela de receitas/massas - **(SECOND TABLE)**:
private static final String TABLE_RECEITA = "receitas";
private static final String COLUMN_TITULO = "titulo";
private static final String COLUMN_ITEM1 = "item1";
private static final String COLUMN_ITEM2 = "item2";
private static final String COLUMN_ITEM3 = "item3";
private static final String COLUMN_ITEM4 = "item4";
private static final String COLUMN_ITEM5 = "item5";
private static final String COLUMN_ITEM6 = "item6";
private static final String COLUMN_ITEM7 = "item7";
private static final String COLUMN_ITEM8 = "item8";
private static final String COLUMN_ITEM9 = "item9";
private static final String COLUMN_ITEM10 = "item10";
private static final String COLUMN_ITEM11 = "item11";
private static final String COLUMN_ITEM12 = "item12";
private static final String COLUMN_ITEM13 = "item13";
private static final String COLUMN_ITEM14 = "item14";
private static final String COLUMN_VALOR1 = "valor1";
private static final String COLUMN_VALOR2 = "valor2";
private static final String COLUMN_VALOR3 = "valor3";
private static final String COLUMN_VALOR4 = "valor4";
private static final String COLUMN_VALOR5 = "valor5";
private static final String COLUMN_VALOR6 = "valor6";
private static final String COLUMN_VALOR7 = "valor7";
private static final String COLUMN_VALOR8 = "valor8";
private static final String COLUMN_VALOR9 = "valor9";
private static final String COLUMN_VALOR10 = "valor10";
private static final String COLUMN_VALOR11 = "valor11";
private static final String COLUMN_VALOR12 = "valor12";
private static final String COLUMN_VALOR13 = "valor13";
private static final String COLUMN_VALOR14 = "valor14";
private static final String COLUMN_QTD1 = "qtd1";
private static final String COLUMN_QTD2 = "qtd2";
private static final String COLUMN_QTD3 = "qtd3";
private static final String COLUMN_QTD4 = "qtd4";
private static final String COLUMN_QTD5 = "qtd5";
private static final String COLUMN_QTD6 = "qtd6";
private static final String COLUMN_QTD7 = "qtd7";
private static final String COLUMN_QTD8 = "qtd8";
private static final String COLUMN_QTD9 = "qtd9";
private static final String COLUMN_QTD10 = "qtd10";
private static final String COLUMN_QTD11 = "qtd11";
private static final String COLUMN_QTD12 = "qtd12";
private static final String COLUMN_QTD13 = "qtd13";
private static final String COLUMN_QTD14 = "qtd14";
MyDatabase(Context context) {
super(context, DATABASE_NAME,null, DATABASE_VERSION);
this.context = context;
}
#Override
public void onCreate(SQLiteDatabase db) {
String query = "CREATE TABLE "+ TABLE_PRODUTO +
" (" + COLUMN_CODIGO + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
COLUMN_PRODUTO + " TEXT, " +
COLUMN_VALOR + " FLOAT, " +
COLUMN_QTD + " FLOAT, " +
COLUMN_TIPO + " TEXT); ";
db.execSQL(query);
String query2 = "CREATE TABLE "+ TABLE_RECEITA +
" (" + COLUMN_TITULO + " TEXT PRIMARY KEY, " + COLUMN_ITEM1 + "TEXT," + COLUMN_ITEM2 + " TEXT, " +
COLUMN_ITEM3 + " TEXT, " + COLUMN_ITEM4 + " TEXT, " + COLUMN_ITEM5 + " TEXT, " + COLUMN_ITEM6 + " TEXT, " +
COLUMN_ITEM7 + " TEXT, " + COLUMN_ITEM8 + " TEXT, " + COLUMN_ITEM9 + " TEXT, " + COLUMN_ITEM10 + " TEXT, " +
COLUMN_ITEM11 + " TEXT, " + COLUMN_ITEM12 + " TEXT, " + COLUMN_ITEM13 + " TEXT, " + COLUMN_ITEM14 + " TEXT, " +
COLUMN_VALOR1 + "FLOAT, " + COLUMN_VALOR2 + "FLOAT, " +COLUMN_VALOR3 + "FLOAT, " + COLUMN_VALOR4 + "FLOAT, " +
COLUMN_VALOR5 + "FLOAT, " + COLUMN_VALOR6 + "FLOAT, " + COLUMN_VALOR7 + "FLOAT, " + COLUMN_VALOR8 + "FLOAT, " +
COLUMN_VALOR9 + "FLOAT, " + COLUMN_VALOR10 + "FLOAT, " + COLUMN_VALOR11 + "FLOAT, " + COLUMN_VALOR12 + "FLOAT, " +
COLUMN_VALOR13 + "FLOAT, " + COLUMN_VALOR14 + "FLOAT, " + COLUMN_QTD1 + "FLOAT, " + COLUMN_QTD1 + "FLOAT, " +
COLUMN_QTD1 + "FLOAT, " + COLUMN_QTD2 + "FLOAT, " + COLUMN_QTD3 + "FLOAT, " + COLUMN_QTD4 + "FLOAT, " +
COLUMN_QTD5 + "FLOAT, " + COLUMN_QTD6 + "FLOAT, " + COLUMN_QTD7 + "FLOAT, " + COLUMN_QTD8 + "FLOAT, " +
COLUMN_QTD9 + "FLOAT, " + COLUMN_QTD10 + "FLOAT, " + COLUMN_QTD11 + "FLOAT, " + COLUMN_QTD12 + "FLOAT, " +
COLUMN_QTD13 + "FLOAT, " + COLUMN_QTD14 + "FLOAT); ";
db.execSQL(query2);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_PRODUTO);
db.execSQL("DROP TABLE IF EXISTS " + TABLE_RECEITA);
onCreate(db);
}
void addProduto(MateriaPrima materiaPrima) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(COLUMN_PRODUTO, materiaPrima.getProduto());
cv.put(COLUMN_VALOR, materiaPrima.getValor());
cv.put(COLUMN_QTD, materiaPrima.getQuantidade());
cv.put(COLUMN_TIPO, materiaPrima.getTipo());
long result = db.insert(TABLE_PRODUTO, null, cv);
if (result == -1) {
Toast.makeText(context, R.string.strFailed, Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(context, R.string.strAddSucess, Toast.LENGTH_SHORT).show();
}
db.close();
}
void addReceita(Receita receita) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(COLUMN_TITULO, receita.getTitulo());
cv.put(COLUMN_ITEM1, receita.getItem1());
cv.put(COLUMN_ITEM2, receita.getItem2());
cv.put(COLUMN_ITEM3, receita.getItem3());
cv.put(COLUMN_ITEM4, receita.getItem4());
cv.put(COLUMN_ITEM5, receita.getItem5());
cv.put(COLUMN_ITEM6, receita.getItem6());
cv.put(COLUMN_ITEM7, receita.getItem7());
cv.put(COLUMN_ITEM8, receita.getItem8());
cv.put(COLUMN_ITEM9, receita.getItem9());
cv.put(COLUMN_ITEM10, receita.getItem10());
cv.put(COLUMN_ITEM11, receita.getItem11());
cv.put(COLUMN_ITEM12, receita.getItem12());
cv.put(COLUMN_ITEM13, receita.getItem13());
cv.put(COLUMN_ITEM14, receita.getItem14());
cv.put(COLUMN_VALOR1, receita.getValor1());
cv.put(COLUMN_VALOR2, receita.getValor2());
cv.put(COLUMN_VALOR3, receita.getValor3());
cv.put(COLUMN_VALOR4, receita.getValor4());
cv.put(COLUMN_VALOR5, receita.getValor5());
cv.put(COLUMN_VALOR6, receita.getValor6());
cv.put(COLUMN_VALOR7, receita.getValor7());
cv.put(COLUMN_VALOR8, receita.getValor8());
cv.put(COLUMN_VALOR9, receita.getValor9());
cv.put(COLUMN_VALOR10, receita.getValor10());
cv.put(COLUMN_VALOR11, receita.getValor11());
cv.put(COLUMN_VALOR12, receita.getValor12());
cv.put(COLUMN_VALOR13, receita.getValor13());
cv.put(COLUMN_VALOR14, receita.getValor14());
cv.put(COLUMN_QTD1, receita.getQtd1());
cv.put(COLUMN_QTD2, receita.getQtd2());
cv.put(COLUMN_QTD3, receita.getQtd3());
cv.put(COLUMN_QTD4, receita.getQtd4());
cv.put(COLUMN_QTD5, receita.getQtd5());
cv.put(COLUMN_QTD6, receita.getQtd6());
cv.put(COLUMN_QTD7, receita.getQtd7());
cv.put(COLUMN_QTD8, receita.getQtd8());
cv.put(COLUMN_QTD9, receita.getQtd9());
cv.put(COLUMN_QTD10, receita.getQtd10());
cv.put(COLUMN_QTD11, receita.getQtd11());
cv.put(COLUMN_QTD12, receita.getQtd12());
cv.put(COLUMN_QTD13, receita.getQtd13());
cv.put(COLUMN_QTD14, receita.getQtd14());
long result = db. insert(TABLE_RECEITA, null, cv);
if (result == -1) {
Toast.makeText(context, R.string.strFailed, Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(context, R.string.strAddSucess, Toast.LENGTH_SHORT).show();
}
db.close();
}
public List<MateriaPrima> buscaProduto() {
String[] columns = {COLUMN_CODIGO, COLUMN_PRODUTO, COLUMN_VALOR, COLUMN_QTD, COLUMN_TIPO};
SQLiteDatabase db = getReadableDatabase();
#SuppressLint("Recycle") Cursor cursor = db.query(TABLE_PRODUTO, columns, null, null, null,null, null);
while (cursor.moveToNext()) {
int index1 = cursor.getColumnIndex(COLUMN_CODIGO);
int codigo = cursor.getInt(index1);
int index2 = cursor.getColumnIndex(COLUMN_PRODUTO);
String produto = cursor.getString(index2);
int index3 = cursor.getColumnIndex(COLUMN_VALOR);
float valor = cursor.getFloat(index3);
int index4 = cursor.getColumnIndex(COLUMN_QTD);
float quantidade = cursor.getFloat(index4);
int index5 = cursor.getColumnIndex(COLUMN_TIPO);
String tipo = cursor.getString(index5);
MateriaPrima produtos = new MateriaPrima(codigo, produto, valor, quantidade, tipo);
listaProduto.add(produtos);
}
return listaProduto;
}
public List<Receita> buscaReceita() {
String[] columns = {COLUMN_TITULO, COLUMN_ITEM1, COLUMN_VALOR, COLUMN_QTD, COLUMN_TIPO};
SQLiteDatabase db = getReadableDatabase();
#SuppressLint("Recycle") Cursor cursor = db.query(TABLE_RECEITA, columns, null, null, null,null, null);
while (cursor.moveToNext()) {
int index1 = cursor.getColumnIndex(COLUMN_TITULO);
String titulo = cursor.getString(index1);
int index2 = cursor.getColumnIndex(COLUMN_ITEM1);
String item1 = cursor.getString(index2);
/*int index3 = cursor.getColumnIndex(COLUMN_VALOR);
float valor = cursor.getFloat(index3);
int index4 = cursor.getColumnIndex(COLUMN_QTD);
float quantidade = cursor.getFloat(index4);
int index5 = cursor.getColumnIndex(COLUMN_TIPO);
String tipo = cursor.getString(index5);*/
Receita receitas = new Receita(titulo, item1); //, valor, quantidade, tipo);
listaReceita.add(receitas);
}
return listaReceita;
}
void updateData(String row_id, String produto, String valor, String quantidade, String tipo){
SQLiteDatabase db = this.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(COLUMN_PRODUTO, produto);
cv.put(COLUMN_VALOR, valor);
cv.put(COLUMN_QTD, quantidade);
cv.put(COLUMN_TIPO, tipo);
long result = db.update(TABLE_PRODUTO, cv, "codigo=?", new String[]{row_id});
if(result == -1){
Toast.makeText(context, R.string.strFailed, Toast.LENGTH_SHORT).show();
} else{
Toast.makeText(context, R.string.strSucess, Toast.LENGTH_SHORT).show();
}
db.close();
}
void deleteOneRow(String row_id){
SQLiteDatabase db = this.getWritableDatabase();
long result = db.delete(TABLE_PRODUTO, "codigo=?", new String[]{row_id});
if(result== -1){
Toast.makeText(context, R.string.strFailed, Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(context, R.string.strSucess, Toast.LENGTH_SHORT).show();
}
}
void deleteAllData() {
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL("DELETE FROM " + TABLE_PRODUTO);
db.close();
}
}
My Activity to Save My Recipe (Second Table) using the ingredients saved on the first table:
package com.myapplication.umdocededaisy;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class AddItem extends AppCompatActivity {
EditText et_produto, et_valor, et_qtd, et_sqtd, et_tipo, et_stipo;
Button btnIncluir;
String produto, valor, quantidade, tipo;
List<MateriaPrima> listaProdutos;
Spinner spinner;
MyDatabase myDB = new MyDatabase(this);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_item);
//Declarações objetos:
et_produto = findViewById(R.id.et_produto);
et_valor = findViewById(R.id.et_valor);
et_qtd = findViewById(R.id.et_qtd);
et_sqtd = findViewById(R.id.et_sqtd);
et_tipo = findViewById(R.id.et_tipo);
et_stipo = findViewById(R.id.et_stipo);
btnIncluir = findViewById(R.id.btnIncluir);
spinner = findViewById(R.id.spinner);
//Chamada de Métodos:
getAndSetIntentData();
loadSpinnerData();
loadSpinner();
//Botões:
//Save Button to Second Table:
btnIncluir.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Receita receita = new Receita();
myDB.addReceita(receita);
}
});
}
void getAndSetIntentData() {
if (getIntent().hasExtra("produto") && getIntent().hasExtra("valor") &&
getIntent().hasExtra("quantidade") && getIntent().hasExtra("tipo")){
//Getting data:
produto = getIntent().getStringExtra("produto");
valor = getIntent().getStringExtra("valor");
quantidade = getIntent().getStringExtra("quantidade");
tipo = getIntent().getStringExtra("tipo");
//Setting data:
et_produto.setText(produto);
et_valor.setText(valor);
et_qtd.setText(quantidade);
et_tipo.setText(tipo);
et_stipo.setText(tipo);
}else{
Toast.makeText(this, R.string.strData0, Toast.LENGTH_SHORT).show();
}
}
I don't know how to write the code to save those changes, I don't know if I have to change something on my database insert method or just here, because my insert method is writing just like the one on my first table.
My AddProduto (where I save my ingredients one first table):
package com.myapplication.umdocededaisy;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
public class AddProduto extends AppCompatActivity {
EditText editProduto, editValor, editQuantidade, editTipo;
FloatingActionButton btnVoltar;
Button btnSalvar;
InputMethodManager inputManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_produto);
//Declarações objetos:
editProduto = findViewById(R.id.editProduto);
editValor = findViewById(R.id.editValor);
editQuantidade = findViewById(R.id.editQuantidade);
editTipo = findViewById(R.id.editTipo);
btnVoltar = findViewById(R.id.btnVoltar);
btnSalvar = findViewById(R.id.btnSalvar);
inputManager = (InputMethodManager) getSystemService (Context.INPUT_METHOD_SERVICE);
btnVoltar.setOnClickListener(v -> {
Intent voltar = new Intent(AddProduto.this, MainActivity.class);
startActivity(voltar);
});
btnSalvar.setOnClickListener(v -> {
if(editProduto.getText().toString().isEmpty() || editValor.getText().toString().isEmpty() ||
editQuantidade.getText().toString().isEmpty() || editTipo.getText().toString().isEmpty())
{
Toast.makeText(AddProduto.this, R.string.strEmpty, Toast.LENGTH_SHORT).show();
}else{
MyDatabase myDB = new MyDatabase(AddProduto.this);
MateriaPrima materiaPrima = new MateriaPrima();
materiaPrima.setProduto(editProduto.getText().toString());
materiaPrima.setValor(Float.parseFloat(editValor.getText().toString()));
materiaPrima.setQuantidade(Float.parseFloat(editQuantidade.getText().toString()));
materiaPrima.setTipo(editTipo.getText().toString());
myDB.addProduto(materiaPrima);
}
reset();
editProduto.requestFocus(); //focar no campo especificado
inputManager.hideSoftInputFromWindow(btnSalvar.getWindowToken(), 0);
});
}
void reset(){
editProduto.setText(" ");
editValor.setText(" ");
editQuantidade.setText(" ");
editTipo.setText(" ");
}
}
Can anyone help me?
I've looked everywhere but didn't found anything that can be applied to my case.
Thanks in advance.
I don't know how to write the code to save those changes,
The design of your database appears to be introducing complexities that would make inserting and retrieving data into/from the second table quite complex.
By the look of it your second table consists of many columns where a relationship could replace the repeated columns (materia/ingredients) and adding such a relationship could perhaps simplify matters.
To look at this you have materia (ingredients) such as Milk Flour Sugar etc. An ingredient can be used in many recipes and a recipe can use many ingredients. As such you likely want a many-many relationship between recipes and ingredients.
So I'd suggest that you consider a mapping table that allows you have to a many to many relationship, so 3 tables instead of 2. Perhaps consider the following:-
CREATE TABLE IF NOT EXISTS materia_prima (codigo INTEGER PRIMARY KEY /* AUTOINCREMENT */,produto TEXT, VALOR FLOAT, QTD FLOAT, TIPO TEXT);
CREATE TABLE IF NOT EXISTS receitas (TITULO TEXT PRIMARY KEY);
CREATE TABLE IF NOT EXISTS receitas_materia (
titulo_map TEXT REFERENCES receitas(titulo) ON DELETE CASCADE ON UPDATE CASCADE,
materia_map INTEGER REFERENCES materia_prima(codigo) ON DELETE CASCADE ON UPDATE CASCADE, receitas_materia_qtd FLOAT,
PRIMARY KEY(titulo_map,materia_map));
The materia_prima table is as it was (except there is no need for the inefficient AUTOINCREMENT so that's been commented out)
The receitas table has been greatly simplified (just 1 column)
The receitas_materia table is the mapping table that is used to signify that an ingredient is used by a recipe.
Foreign Key constraints (the REFERENCES clause) have been added to enforce referential integrity.
The ON DELETE/UPDATE says to propagate changes from the parent to the child.
A third column has been added to denote the quantity used by the recipe.
So taking this further the following data is added :-
First some ingredients including their cost per measure:-
INSERT INTO materia_prima VALUES
(null,'Plain Flour',0.54,1,'Gram'),
(null,'SR Flour',0.55,1,'Gram'),
(null,'Eggs',1.34,1,'Each'),
(null,'Milk',0.2,1,'MilliLitre'),
(null,'Sugar',0.65,1,'Gram');
Second Some recipes :-
INSERT INTO receitas VALUES ('Bread'),('Cake'),('Scone');
Now the ingredients used by recipes (just Bread and Cakes) :-
INSERT INTO receitas_materia VALUES
('Bread',1 /*map to Plain Flour*/,200 /* uses 200 grams */),
('Bread',3,2),
('Bread',4,50),
('Bread',5,10),
('Cake',2,400),
('Cake',3,3),
('Cake',4,100),
('Cake',5,300)
;
So instead of many columns and the complexity of trying to determine what goes into what column. You simply add as many ingredients per recipe as is required (and let the queries do the work).
A Query such as :-
SELECT *, valor * receitas_materia_qtd AS cost
FROM receitas JOIN receitas_materia ON receitas_materia.titulo_map = receitas.titulo JOIN materia_prima ON materia_prima.codigo = receitas_materia.materia_map;
Could be used to list the product and it's ingredients including the cost per ingredient e.g. using the above the results is:-
e.g. Bread uses 200g of sugar which costs 0.54 per gram so therefore the cost of sugar for bread is 108.
You would appear to want the total cost per item so a query something like :-
SELECT titulo, group_concat(produto||' ('||receitas_materia_qtd||' '||tipo||')') AS materias, sum(valor * receitas_materia_qtd) AS total_cost
FROM receitas JOIN receitas_materia ON receitas_materia.titulo_map = receitas.titulo JOIN materia_prima ON materia_prima.codigo = receitas_materia.materia_map
GROUP BY titulo;
Could be used, this results in :-
Android based Example
The following is a basic example that allows ingredients to be added to a recipe via a spinner. The current ingredients are also listed allowing an ingredient to also be removed by long clicking the ingredient.
The example utilises Cursor Adapters which are simpler to work with.
First is MyDatabase which uses the suggested design and includes methods the various methods:-
public class MyDatabase extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "BancoDoceDaisy.db";
private static final int DATABASE_VERSION = 9;
SQLiteDatabase db;
public static final String TABLE_PRODUTO = "materia_prima";
public static final String COLUMN_PRODUTO_CODIGO = BaseColumns._ID; /*CHANGED Use the standard Android ID column name */
public static final String COLUMN_PRODUTO = "produto";
public static final String COLUMN_VALOR = "valor";
public static final String COLUMN_QTD = "quantidade";
public static final String COLUMN_TIPO = "tipo";
public static final String TABLE_RECEITA = "receitas";
public static final String COLUMN_RECEITA_CODIGO = BaseColumns._ID;
public static final String COLUMN_TITULO = "titulo";
public static final String TABLE_RECEITA_MATERIA_MAP = TABLE_RECEITA + "_" + TABLE_PRODUTO + "_map";
public static final String COLUMN_RECEITA_MAP = TABLE_RECEITA + "_map";
public static final String COLUMN_PRODUTO_MAP = TABLE_PRODUTO + "_map";
public static final String COLUMN_RECEITA_QTD = TABLE_RECEITA + "_" + COLUMN_QTD;
public static final String DERIVED_COLUMN_COST = "cost";
private static final String cost_calculate = TABLE_PRODUTO + "." + COLUMN_VALOR + " * " + TABLE_RECEITA_MATERIA_MAP + "." + COLUMN_RECEITA_QTD + " AS " + DERIVED_COLUMN_COST;
public static final String DERIVED_COLUMN_TOTAL_COST = "total_cost";
private static final String total_cost_calculate = "SUM(" + TABLE_PRODUTO + "." + COLUMN_VALOR + " * " + TABLE_RECEITA_MATERIA_MAP + "." + COLUMN_QTD + ") AS " + DERIVED_COLUMN_TOTAL_COST;
private final String join_receitamateria_map =
" JOIN " + TABLE_RECEITA_MATERIA_MAP +
" ON " + TABLE_RECEITA + "." + COLUMN_RECEITA_CODIGO +
" = " + TABLE_RECEITA_MATERIA_MAP + "." + COLUMN_RECEITA_MAP;
private final String join_produto =
" JOIN " + TABLE_PRODUTO +
" ON " + TABLE_RECEITA_MATERIA_MAP + "." + COLUMN_PRODUTO_MAP +
" = " + TABLE_PRODUTO + "." + COLUMN_PRODUTO_CODIGO;
private static final String fkey_options = " ON DELETE CASCADE ON UPDATE CASCADE ";
private static volatile MyDatabase instance = null;
private MyDatabase(#Nullable Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
db = this.getWritableDatabase();
}
public static MyDatabase getDatabaseInstance(Context context) {
if (instance == null) {
instance = new MyDatabase(context);
}
return instance;
}
#Override
public void onCreate(SQLiteDatabase db) {
String query = "CREATE TABLE "+ TABLE_PRODUTO +
" (" + COLUMN_PRODUTO_CODIGO + " INTEGER PRIMARY KEY, " +
COLUMN_PRODUTO + " TEXT, " +
COLUMN_VALOR + " FLOAT, " +
COLUMN_QTD + " FLOAT, " +
COLUMN_TIPO + " TEXT); ";
db.execSQL(query);
query = "CREATE TABLE IF NOT EXISTS " + TABLE_RECEITA + "(" +
COLUMN_RECEITA_CODIGO + " INTEGER PRIMARY KEY ," + /* ADDED to make things simpler android wise */
COLUMN_TITULO + " TEXT UNIQUE" +
")";
db.execSQL(query);
query = "CREATE TABLE IF NOT EXISTS " +TABLE_RECEITA_MATERIA_MAP + "(" +
COLUMN_RECEITA_MAP + " INTEGER REFERENCES " + TABLE_RECEITA + "(" + COLUMN_PRODUTO_CODIGO + ")" + fkey_options + "," +
COLUMN_PRODUTO_MAP + " INTEGER REFERENCES " + TABLE_PRODUTO + "(" + COLUMN_RECEITA_CODIGO + ")" + fkey_options + "," +
COLUMN_RECEITA_QTD + " FLOAT, " +
"PRIMARY KEY(" + COLUMN_RECEITA_MAP + "," + COLUMN_PRODUTO_MAP + ")" +
")";
db.execSQL(query);
}
#Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {
}
public long insertProduto(String produto,Float valor, Float qtd, String tipo) {
ContentValues cv = new ContentValues();
cv.put(COLUMN_PRODUTO,produto);
cv.put(COLUMN_VALOR,valor);
cv.put(COLUMN_QTD,qtd);
cv.put(COLUMN_TIPO,tipo);
return db.insert(TABLE_PRODUTO,null,cv);
}
public long insertReceita(String titulo) {
ContentValues cv = new ContentValues();
cv.put(COLUMN_TITULO,titulo);
return db.insert(TABLE_RECEITA,null,cv);
}
public long insertReceitaMateria(long receitaMap, long produtoMap, Float qtd) {
ContentValues cv = new ContentValues();
cv.put(COLUMN_RECEITA_MAP,receitaMap);
cv.put(COLUMN_PRODUTO_MAP,produtoMap);
cv.put(COLUMN_RECEITA_QTD,qtd);
return db.insert(TABLE_RECEITA_MATERIA_MAP,null,cv);
}
public int deleteMateriaFromReceita(long receita, long produtoCodigo) {
return db.delete(TABLE_RECEITA_MATERIA_MAP,COLUMN_RECEITA_MAP + " =? AND " + COLUMN_PRODUTO_MAP + "=?",new String[]{String.valueOf(receita), String.valueOf(produtoCodigo)});
}
public Cursor getProduto() {
return db.query(TABLE_PRODUTO,null,null,null,null,null,null);
}
public Cursor getReceita() {
return db.query(TABLE_RECEITA,null,null,null,null,null,null);
}
public Cursor getReceitaProduto(Long receita) {
return db.query(
TABLE_RECEITA + join_receitamateria_map + join_produto,
new String[]{"*",cost_calculate},
TABLE_RECEITA_MATERIA_MAP + "." + COLUMN_RECEITA_MAP + "=?",new String[]{receita.toString()},null,null,null);
}
public String getReceitanameByCodigo(long codigo) {
String rv = "NOT KNOWN";
Cursor csr =db.query(TABLE_RECEITA,new String[]{COLUMN_TITULO},COLUMN_RECEITA_CODIGO + "=?",new String[]{String.valueOf(codigo)},null,null,null);
if (csr.moveToFirst()) {
rv = csr.getString(csr.getColumnIndex(COLUMN_TITULO));
}
csr.close();
return rv;
}
}
Next is an initial activity MainActivity (designed to only run once) that adds some materia/produtos (ingredients) and some blank (no ingredients) receitas (recipes). After which the AddProduto activity is invoked passing the Cake recipe to the activity :-
public class MainActivity extends AppCompatActivity {
MyDatabase db;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = MyDatabase.getDatabaseInstance(this);
db.insertProduto("Plain Flour",0.54F,1F,"Grams");
db.insertProduto("SR Flour",0.55F,1F,"Grams");
db.insertProduto("Eggs",1.34F,1f,"Each");
db.insertProduto("Milk",0.2F,1F,"ml");
db.insertProduto("Sugar",0.65F,1F,"Grams");
db.insertReceita("Bread");
long selectedId = db.insertReceita("Cake");
db.insertReceita("Scone");
Intent intent = new Intent(this,AddProduto.class);
intent.putExtra(MyDatabase.TABLE_RECEITA,selectedId);
startActivity(intent);
}
}
Finally is the AddProduto activity. This has a spinner that allows selection of an ingredient(s). Selecting an ingredient adds the ingredient to the recipe and the recipe's ingredients are then refreshed to show the added ingredient. If an ingredient in the list of ingredients is long clicked it is removed from the recipe.
Note only very basic management is incorporated. e.g. really the ingredients in the spinner should exclude those already in the recipe.
The amount/quantity of the ingredient is set to be 100 for simplicity/brevity of the demo/example:-
public class AddProduto extends AppCompatActivity {
TextView receita;
Spinner materia_add;
ListView materia_in;
SimpleCursorAdapter adapter_list, adapter_spinner;
MyDatabase db;
Cursor csr_add, csr_in;
long current_receita;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_produto);
db = MyDatabase.getDatabaseInstance(this);
receita = this.findViewById(R.id.Receita);
current_receita = this.getIntent().getLongExtra(MyDatabase.TABLE_RECEITA,0);
receita.setText(db.getReceitanameByCodigo(current_receita));
materia_add = this.findViewById(R.id.materia_add);
materia_in = this.findViewById(R.id.materia_in);
setOrRefreshSpinner();
setOrRefreshReceitaList();
}
// Handling the Spinner
private void setOrRefreshSpinner() {
csr_add = db.getProduto();
if (adapter_spinner == null) {
adapter_spinner = new SimpleCursorAdapter(
this,
android.R.layout.simple_list_item_2,
csr_add,
new String[]{MyDatabase.COLUMN_PRODUTO, MyDatabase.COLUMN_VALOR},
new int[]{android.R.id.text1, android.R.id.text2},0
);
materia_add.setAdapter(adapter_spinner);
materia_add.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
db.insertReceitaMateria(current_receita,l,100F); // Made up value for demo
setOrRefreshReceitaList(); // Update the List of materia in the receita
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
} else {
adapter_spinner.swapCursor(csr_add);
}
}
// Handling the recipe ListView
private void setOrRefreshReceitaList() {
csr_in = db.getReceitaProduto(current_receita);
if (adapter_list == null) {
adapter_list = new SimpleCursorAdapter(
this,
android.R.layout.simple_list_item_2,
csr_in,
new String[]{MyDatabase.COLUMN_PRODUTO,MyDatabase.DERIVED_COLUMN_COST},
new int[]{android.R.id.text1,android.R.id.text2},
0
);
materia_in.setAdapter(adapter_list);
materia_in.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
db.deleteMateriaFromReceita(current_receita,l);
setOrRefreshReceitaList();
return true;
}
});
} else {
adapter_list.swapCursor(csr_in);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
csr_in.close();
csr_add.close();
}
}
Result
When first run the AddProduto activity is displayed (after MainActivity starts it) :-
Note that handling the fact that the Spinner automatically selects an Item has not been included so an ingredient is added before any selection is made (see Android Spinner : Avoid onItemSelected calls during initialization in regrad to avoiding this).
Purple is the Spinner
teal is the recipe's list of ingredients (with the total cost of the ingredient i.e. 100 * the ingredient cost).
Select Eggs from the Spinner :-
And then after selection (adding Eggs) :-
LongClick Plain Flour in the List of ingredients :-
I have two classes:
Database class:
package com.qstra.soamazingtodoapp;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class DBAdapter {
private static final String TAG = "DBAdapter"; // used for logging database
// version changes
// Field Names:
public static final String KEY_ROWID = "_id";
public static final String KEY_TASK = "task";
public static final String KEY_DATE = "date";
public static final String KEY_HOUR = "hour";
public static final String KEY_MINUTE = "minute";
public static final String[] ALL_KEYS = new String[] { KEY_ROWID, KEY_TASK,
KEY_DATE, KEY_HOUR, KEY_MINUTE };
// Column Numbers for each Field Name:
public static final int COL_ROWID = 0;
public static final int COL_TASK = 1;
public static final int COL_DATE = 2;
public static final int COL_HOUR = 3;
public static final int COL_MINUTE = 4;
// DataBase info:
public static final String DATABASE_NAME = "dbToDo";
public static final String DATABASE_TABLE = "mainToDo";
public static final int DATABASE_VERSION = 2; // The version number must be
// incremented each time a
// change to DB structure
// occurs.
// SQL statement to create database
private static final String DATABASE_CREATE_SQL = "CREATE TABLE "
+ DATABASE_TABLE + " (" + KEY_ROWID
+ " INTEGER PRIMARY KEY AUTOINCREMENT, " + KEY_TASK
+ " TEXT NOT NULL, " + KEY_DATE + " TEXT" + KEY_HOUR + " TEXT"
+ KEY_MINUTE + " TEXT" + ");";
private final Context context;
private DatabaseHelper myDBHelper;
private SQLiteDatabase db;
public DBAdapter(Context ctx) {
this.context = ctx;
myDBHelper = new DatabaseHelper(context);
}
// Open the database connection.
public DBAdapter open() {
db = myDBHelper.getWritableDatabase();
return this;
}
// Close the database connection.
public void close() {
myDBHelper.close();
}
// Add a new set of values to be inserted into the database.
public long insertRow(String task, String date, String hour, String minute) {
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_TASK, task);
initialValues.put(KEY_DATE, date);
initialValues.put(KEY_HOUR, hour);
initialValues.put(KEY_MINUTE, minute);
// Insert the data into the database.
return db.insert(DATABASE_TABLE, null, initialValues);
}
// Delete a row from the database, by rowId (primary key)
public boolean deleteRow(long rowId) {
String where = KEY_ROWID + "=" + rowId;
return db.delete(DATABASE_TABLE, where, null) != 0;
}
public void deleteAll() {
Cursor c = getAllRows();
long rowId = c.getColumnIndexOrThrow(KEY_ROWID);
if (c.moveToFirst()) {
do {
deleteRow(c.getLong((int) rowId));
} while (c.moveToNext());
}
c.close();
}
// Return all data in the database.
public Cursor getAllRows() {
String where = null;
Cursor c = db.query(true, DATABASE_TABLE, ALL_KEYS, where, null, null,
null, null, null);
if (c != null) {
c.moveToFirst();
}
return c;
}
// Get a specific row (by rowId)
public Cursor getRow(long rowId) {
String where = KEY_ROWID + "=" + rowId;
Cursor c = db.query(true, DATABASE_TABLE, ALL_KEYS, where, null, null,
null, null, null);
if (c != null) {
c.moveToFirst();
}
return c;
}
// Change an existing row to be equal to new data.
public boolean updateRow(long rowId, String task, String date, String hour, String minute) {
String where = KEY_ROWID + "=" + rowId;
ContentValues newValues = new ContentValues();
newValues.put(KEY_TASK, task);
newValues.put(KEY_DATE, date);
newValues.put(KEY_HOUR, hour);
newValues.put(KEY_MINUTE, minute);
// Insert it into the database.
return db.update(DATABASE_TABLE, newValues, where, null) != 0;
}
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase _db) {
_db.execSQL(DATABASE_CREATE_SQL);
}
#Override
public void onUpgrade(SQLiteDatabase _db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading application's database from version "
+ oldVersion + " to " + newVersion
+ ", which will destroy all old data!");
// Destroy old database:
_db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE);
// Recreate new database:
onCreate(_db);
}
}
}
MainActivity class:
package com.qstra.soamazingtodoapp;
import com.qstratwo.soamazingtodoapp.R;
import android.app.Activity;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.database.Cursor;
import android.os.Bundle;
import android.text.TextUtils;
import android.text.format.Time;
import android.view.View;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TimePicker;
import android.widget.Toast;
public class MainActivity extends Activity {
Time today = new Time(Time.getCurrentTimezone());
DBAdapter myDb;
EditText etTasks;
static final int DIALOG_ID = 0;
int hour_x;
int minute_x;
String string_hour_x, string_minute_x="None";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etTasks = (EditText) findViewById(R.id.editText1);
openDB();
listViewItemClick();
listViewItemLongClick();
populateListView();
// setNotification();
}
#Override
protected Dialog onCreateDialog(int idD){
if (idD== DIALOG_ID){
return new TimePickerDialog(MainActivity.this, kTimePickerListener, hour_x,minute_x, false);
}
return null;
}
protected TimePickerDialog.OnTimeSetListener kTimePickerListener =
new TimePickerDialog.OnTimeSetListener() {
#Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
hour_x=hourOfDay;
minute_x=minute;
string_hour_x = Integer.toString(hour_x);
string_minute_x = Integer.toString(minute_x);
Toast.makeText(MainActivity.this, hour_x+" : "+ minute_x,Toast.LENGTH_LONG).show();
}
};
public void setNotification(View v) {
showDialog(DIALOG_ID);
}
private void openDB() {
myDb = new DBAdapter(this);
myDb.open();
}
public void onClick_AddTask (View v) {
today.setToNow();
String timestamp = today.format("%Y-%m-%d %H:%m:%s");
if(!TextUtils.isEmpty(etTasks.getText().toString())) {
myDb.insertRow(etTasks.getText().toString(),timestamp,string_hour_x, string_minute_x);
}
populateListView();
}
private void populateListView() {
Cursor cursor = myDb.getAllRows();
String[] fromFieldNames=new String[] {
DBAdapter.KEY_ROWID, DBAdapter.KEY_TASK };
int[] toViewIDs = new int[] {
R.id.textViewItemNumber, R.id.textViewItemTasks};
SimpleCursorAdapter myCursorAdapter;
myCursorAdapter = new SimpleCursorAdapter(getBaseContext(), R.layout.item_layout, cursor, fromFieldNames, toViewIDs, 0);
ListView myList = (ListView) findViewById(R.id.listViewTask);
myList.setAdapter(myCursorAdapter);
}
private void updateTask(long id){
Cursor cursor = myDb.getRow(id);
if (cursor.moveToFirst()){
String task = etTasks.getText().toString(); // POBIERANIE Z TEXTFIELD
today.setToNow();
String date = today.format("%Y-%m-%d %H:%m");
// String string_minute_x= Integer.toString(minute_x);
// String string_houte_x=Integer.toString(hour_x);
myDb.updateRow(id, task, date, string_hour_x, string_minute_x );
}
cursor.close();
}public void onClick_DeleteTasks(View v) {
myDb.deleteAll();
populateListView();
}
public void onClick_AppClose(View v) {
moveTaskToBack(true);
MainActivity.this.finish();
}
public void listViewItemLongClick(){
ListView myList = (ListView) findViewById(R.id.listViewTask);
myList.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
int arg2, long id) {
// TODO Auto-generated method stub
myDb.deleteRow(id);
populateListView();
return false;
}
});
}
private void listViewItemClick(){
ListView myList = (ListView) findViewById(R.id.listViewTask);
myList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long id) {
updateTask(id);
populateListView();
displayToast(id);
}
});
}
private void displayToast(long id){
Cursor cursor = myDb.getRow(id);
if(cursor.moveToFirst()) {
long idDB = cursor.getLong(DBAdapter.COL_ROWID);
String task = cursor.getString(DBAdapter.COL_TASK);
String date = cursor.getString(DBAdapter.COL_DATE);
String message = "ID: " + idDB + "\n" + "Task: " + task + "\n" + "Date: " + date;
Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show();
}
cursor.close();
}
}
(getting id and text from texfield to database works fine but..)
I'm trying to get hour and minutes values from TimePickerDialog and insert in to database but it seems not working.
Screen shot from log cat:
Why can't it see column named 'hour'?
Add commas in your statement to create database
// SQL statement to create database
private static final String DATABASE_CREATE_SQL = "CREATE TABLE "
+ DATABASE_TABLE + " (" + KEY_ROWID
+ " INTEGER PRIMARY KEY AUTOINCREMENT, " + KEY_TASK
+ " TEXT NOT NULL, " + KEY_DATE + " TEXT," + KEY_HOUR + " TEXT,"
+ KEY_MINUTE + " TEXT" + ")";
I wanted to play with a ContentProvider example but I ran into an issue I can't seem to solve.
This example consists of an Activity:
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.support.v4.widget.SimpleCursorAdapter;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
public class MainActivity extends ActionBarActivity
{
final String LOG_TAG = "myLogs";
final Uri CONTACT_URI = Uri.parse("content://zulfigarov.com.trainingprj.MyContactsProvider/contacts");
final String CONTACT_NAME = "name";
final String CONTACT_EMAIL = "email";
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Cursor cursor = getContentResolver().query(CONTACT_URI, null, null, null, null);
startManagingCursor(cursor);
String[] from = {"name", "email"};
int[] to = {android.R.id.text1, android.R.id.text2};
SimpleCursorAdapter adapter
= new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, cursor, from, to, 0);
ListView lvContact = (ListView)findViewById(R.id.lvContacts);
lvContact.setAdapter(adapter);
}
public void onClickInsert(View view)
{
ContentValues cv = new ContentValues();
cv.put(CONTACT_NAME, "name 4");
cv.put(CONTACT_EMAIL, "email 4");
Uri newUri = getContentResolver().insert(CONTACT_URI, cv);
Log.d(LOG_TAG, "insert, result Uri: " + newUri.toString());
}
public void onClickUpdate(View view)
{
ContentValues cv = new ContentValues();
cv.put(CONTACT_NAME, "name 5");
cv.put(CONTACT_EMAIL, "email 5");
Uri uri = ContentUris.withAppendedId(CONTACT_URI, 2);
int cnt = getContentResolver().update(uri, cv, null, null);
Log.d(LOG_TAG, "update, count = " + cnt);
}
public void onClickDelete(View view)
{
Uri uri = ContentUris.withAppendedId(CONTACT_URI, 3);
int cnt = getContentResolver().delete(uri, null, null);
Log.d(LOG_TAG, "delete, count = " + cnt);
}
public void onClickError(View view)
{
Uri uri = Uri.parse("content://zulfigarov.com.trainingprj.MyContentProvider/phones");
try
{
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
}
catch (Exception ex)
{
Log.d(LOG_TAG, "Error: " + ex.getClass() + ", " + ex.getMessage());
}
}
}
and a ContentProvider:
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.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
public class MyContactsProvider extends ContentProvider
{
final String LOG_TAG = "myLogs";
static final String DB_NAME = "mydb";
static final int DB_VERSION = 1;
static final String CONTACT_TABLE = "contacts";
static final String CONTACT_ID = "_id";
static final String CONTACT_NAME = "name";
static final String CONTACT_EMAIL = "email";
static final String DB_CREATE = "CREATE TABLE " + CONTACT_TABLE + "("
+ CONTACT_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ CONTACT_NAME + " TEXT, "
+ CONTACT_EMAIL + " TEXT" + ");";
static final String AUTHORITY = "zulfigarov.com.trainingprj.MyContactsProvider";
static final String CONTACT_PATH = "contacts";
public static final Uri CONTACT_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + CONTACT_PATH);
static final String CONTACT_CONTENT_TYPE = "vnd.android.cursor.dir/vnd." + AUTHORITY + "." + CONTACT_PATH;
static final String CONTACT_CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd." + AUTHORITY + "." + CONTACT_PATH;
static final int URI_CONTACTS = 1;
static final int URI_CONTACTS_ID = 2;
private static final UriMatcher uriMatcher;
static
{
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, CONTACT_PATH, URI_CONTACTS);
uriMatcher.addURI(AUTHORITY, CONTACT_PATH + "/#", URI_CONTACTS_ID);
}
DBHelper dbHelper;
SQLiteDatabase db;
#Override
public boolean onCreate()
{
Log.d(LOG_TAG, "onCreate provider");
dbHelper = new DBHelper(getContext());
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
{
Log.d(LOG_TAG,"query, " + uri.toString());
switch (uriMatcher.match(uri))
{
case URI_CONTACTS:
Log.d(LOG_TAG, "URI_CONTACTS");
if(TextUtils.isEmpty(sortOrder))
{
sortOrder = CONTACT_NAME + " ASC";
}
break;
case URI_CONTACTS_ID:
String id = uri.getLastPathSegment();
Log.d(LOG_TAG, "URI_CONTACTS_ID");
if (TextUtils.isEmpty(selection))
selection = CONTACT_ID + " = " + id;
else
selection = selection + " AND " + CONTACT_ID + " = " + id;
break;
default:
throw new IllegalArgumentException("Wrong URI: " + uri);
}
db = dbHelper.getWritableDatabase();
Cursor cursor = db.query(CONTACT_TABLE, projection, selection,
selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(),CONTACT_CONTENT_URI);
return cursor;
}
#Override
public String getType(Uri uri)
{
Log.d(LOG_TAG, "getTYpe, " + uri.toString());
switch (uriMatcher.match(uri))
{
case URI_CONTACTS:
return CONTACT_CONTENT_TYPE;
case URI_CONTACTS_ID:
return CONTACT_CONTENT_ITEM_TYPE;
}
return null;
}
#Override
public Uri insert(Uri uri, ContentValues values)
{
Log.d(LOG_TAG,"insert, " + uri.toString());
if(uriMatcher.match(uri) != URI_CONTACTS)
throw new IllegalArgumentException("Wrong URI: " + uri);
db = dbHelper.getWritableDatabase();
long rowID = db.insert(CONTACT_TABLE, null, values);
Uri resultUri = ContentUris.withAppendedId(CONTACT_CONTENT_URI, rowID);
getContext().getContentResolver().notifyChange(resultUri, null);
return resultUri;
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs)
{
Log.d(LOG_TAG,"delete, " + uri.toString());
switch (uriMatcher.match(uri))
{
case URI_CONTACTS:
Log.d(LOG_TAG,"URI_CONTACTS");
break;
case URI_CONTACTS_ID:
String id = uri.getLastPathSegment();
Log.d(LOG_TAG,"URI_CONTACTS_ID, " + id);
if(TextUtils.isEmpty(selection))
{
selection = CONTACT_ID + " = " + id;
}
else
{
selection = selection + " AND " + CONTACT_ID + " = " + id;
}
break;
default:
throw new IllegalArgumentException("Wrong URI: " + uri);
}
db = dbHelper.getWritableDatabase();
int cnt = db.delete(CONTACT_TABLE, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
return cnt;
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
{
Log.d(LOG_TAG,"update, " + uri.toString());
switch (uriMatcher.match(uri))
{
case URI_CONTACTS:
Log.d(LOG_TAG,"URI_CONTACTS");
break;
case URI_CONTACTS_ID:
String id = uri.getLastPathSegment();
Log.d(LOG_TAG, "URI_CONTACTS_ID");
if(TextUtils.isEmpty(selection))
{
selection = CONTACT_ID;
}
else
{
selection = selection + " AND " + CONTACT_ID + " = " + id;
}
break;
default:
throw new IllegalArgumentException("Wrong URI: " + uri);
}
db = dbHelper.getWritableDatabase();
int cnt = db.update(CONTACT_TABLE, values, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri,null);
return cnt;
}
private class DBHelper extends SQLiteOpenHelper
{
public DBHelper(Context context)
{
super(context, DB_NAME, null, DB_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db)
{
db.execSQL(DB_CREATE);
ContentValues cv = new ContentValues();
for (int i = 1; i <= 3; i++)
{
cv.put(CONTACT_NAME, "name " + i);
cv.put(CONTACT_EMAIL, "email " + i);
db.insert(CONTACT_TABLE, null, cv);
}
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
}
}
}
The touble is that when I click on the buttons (firing onClickInsert, onClickUpdate etc. methods in MainActivity) it updates the data in database but doesn't update the ListView on an activity. Looks like
getContext().getContentResolver().notifyChange(resultUri, null);
is not working properly. So I can't find where i'm wrong.
Use CursorLoaders to load the data and populate the ListView.
http://developer.android.com/reference/android/content/CursorLoader.html
Then use getContext().getContentResolver().notifyChange(resultUri, null); when you insert, update or delete!
Without CursorLoaders you will have to use ContentObservers
So, although DB is updated in background and you are using notifyChange() but no one is listening to that!
You should be calling adapter.notifyDataSetChanged() (after the onClickInsert) in order to make your ListView notice about the change.
I can create the database just fine, I can Insert and view the values, but whenever I close the android virtual device in eclipse and reopen it, the database values are gone! :O
Please help me!
There are 3 classes : (1 that handles the database and 2 activities)
It would mean the world to me!!! :O
I'd +rep if I coudl
Thank you guys sooo much!#!!!
Here is my code
Database manager :`
package com.jeux;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class Database2 {
private static final String DATABASE_NAME = "JeuxMotActivity";
private static final int DATABASE_VERSION = 1;
public static final String TABLE_CATEGORIE = "Categorie";
public static final String KEY_CATEGORIE_ID_CATEGORIE = "IDCategorie";
public static final String KEY_CATEGORIE_NOM_CATEGORIE = "NomCategorie";
public static final String TABLE_MOT = "Mot";
public static final String KEY_MOT_ID_MOT = "IDMot";
public static final String KEY_MOT_MOT = "Mot";
public static final String KEY_MOT_EMAIL = "Email";
public static final String KEY_MOT_ID_CATEGORIE = "IDCategorie";
public static final String TABLE_ADMINISTRATEUR = "Administrateur";
public static final String KEY_ADMINISTRATEUR_EMAIL = "Email";
public static final String KEY_ADMINISTRATEUR_PASSWORD = "Password";
public static final String TABLE_SCORE = "Score";
public static final String KEY_SCORE_ID_JEU = "IDJeu";
public static final String KEY_SCORE_USERNAME = "Username";
public static final String KEY_SCORE_SCORE = "Score";
public static final String KEY_SCORE_INITIALES = "Initiales";
public static final String TABLE_UTILISATEUR = "Utilisateur";
public static final String KEY_UTILISATEUR_USERNAME = "Username";
public static final String KEY_UTILISATEUR_PASSWORD = "Password";
public static final String TABLE_JEU = "jeu";
public static final String KEY_JEU_ID_JEU = "IDJeu";
public static final String KEY_JEU_NOM_JEU = "NomJeu";
public static final String KEY_JEU_COMMENTAIRES = "Commentaires";
private DBHelper ourHelper;
private final Context ourContext;
private SQLiteDatabase ourDatabase;
private static class DBHelper extends SQLiteOpenHelper {
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(android.database.sqlite.SQLiteDatabase nb) {
//TODO check teh constraints and the relations
nb.execSQL("CREATE TABLE " + TABLE_CATEGORIE + " (" +
KEY_CATEGORIE_ID_CATEGORIE + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
KEY_CATEGORIE_NOM_CATEGORIE + " TEXT NOT NULL" + ");"
);
nb.execSQL("CREATE TABLE " + TABLE_MOT + " (" +
KEY_MOT_ID_MOT + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
KEY_MOT_MOT + " TEXT NOT NULL," +
KEY_MOT_EMAIL + " TEXT NOT NULL," +
KEY_MOT_ID_CATEGORIE + " TEXT NOT NULL," +
"FOREIGN KEY(" + KEY_MOT_ID_CATEGORIE + ") REFERENCES " + TABLE_CATEGORIE + "(" + KEY_CATEGORIE_ID_CATEGORIE + ")," +
"FOREIGN KEY(" + KEY_MOT_EMAIL + ") REFERENCES " + TABLE_ADMINISTRATEUR + "(" + KEY_ADMINISTRATEUR_EMAIL + ")" +
");"
);
nb.execSQL("CREATE TABLE " + TABLE_ADMINISTRATEUR + " (" +
KEY_ADMINISTRATEUR_EMAIL + " TEXT PRIMARY KEY , " +
KEY_ADMINISTRATEUR_PASSWORD + " TEXT NOT NULL" + ");"
);
nb.execSQL("CREATE TABLE " + TABLE_SCORE + " (" +
KEY_SCORE_ID_JEU + " TEXT NOT NULL, " +
KEY_SCORE_USERNAME + " TEXT NOT NULL," +
KEY_SCORE_SCORE + " INTEGER NOT NULL," +
KEY_SCORE_INITIALES + " TEXT NOT NULL," +
"FOREIGN KEY(" + KEY_SCORE_ID_JEU + ") REFERENCES " + TABLE_JEU + "(" + KEY_JEU_ID_JEU + ")," +
"FOREIGN KEY(" + KEY_SCORE_USERNAME + ") REFERENCES " + TABLE_UTILISATEUR + "(" + KEY_UTILISATEUR_USERNAME + ")" +
");"
);
nb.execSQL("CREATE TABLE " + TABLE_UTILISATEUR + " (" +
KEY_UTILISATEUR_USERNAME + " TEXT PRIMARY KEY , " +
KEY_UTILISATEUR_PASSWORD + " TEXT NOT NULL" + ");"
);
nb.execSQL("CREATE TABLE " + TABLE_JEU + " (" +
KEY_JEU_ID_JEU + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
KEY_JEU_NOM_JEU + " TEXT NOT NULL UNIQUE," +
KEY_JEU_COMMENTAIRES + " TEXT" +
");"
);
}
#Override
public void onUpgrade(android.database.sqlite.SQLiteDatabase db,
int oldversion, int newversion) {
// db.execSQL("DROP TABLE IF EXISTS " + TABLE_CATEGORIE);
// onCreate(db);
}
}
public Database2(Context c){
ourContext = c;
}
public Database2 open() throws SQLException {
ourHelper = new DBHelper(ourContext);
ourDatabase = ourHelper.getWritableDatabase();
return this;
}
public void close() throws SQLException{
ourHelper.close();
}
public long createCategorie(String name) {
ContentValues cv = new ContentValues();
cv.put(KEY_CATEGORIE_NOM_CATEGORIE, name);
return ourDatabase.insert(TABLE_CATEGORIE, null, cv);
}
public String getCategorieData(){
String result = "";
String[] columns = new String[] {KEY_CATEGORIE_ID_CATEGORIE, KEY_CATEGORIE_NOM_CATEGORIE};
Cursor c = ourDatabase.query(TABLE_CATEGORIE, columns,null, null, null, null, null);
int id = c.getColumnIndex(KEY_CATEGORIE_ID_CATEGORIE);
int name = c.getColumnIndex(KEY_CATEGORIE_NOM_CATEGORIE);
for(c.moveToFirst(); !c.isAfterLast(); c.moveToNext()){
result += c.getString(id) + " " + c.getString(name) + "\n";
}
return result;
}
}
`
Event Manager :
package com.jeux;
import android.app.Activity;
import android.app.Dialog;
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;
import android.widget.TextView;
public class Jeuxdemots2Activity extends Activity implements OnClickListener {
Button sqlUpdate, sqlView;
EditText nameText;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
sqlUpdate = (Button) findViewById(R.id.updatebutton);
sqlView = (Button) findViewById(R.id.afficherbutton);
nameText = (EditText) findViewById(R.id.nametext);
sqlUpdate.setOnClickListener(this);
sqlView.setOnClickListener(this);
}
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
switch (arg0.getId()){
case R.id.updatebutton:
try{
String name = nameText.getText().toString();
Database2 entry = new Database2(Jeuxdemots2Activity.this);
entry.open();
entry.createCategorie(name);
entry.close();
}catch (Exception e){
String error = e.toString();
Dialog d = new Dialog(this);
d.setTitle("Dang it!");
TextView tv = new TextView(this);
tv.setText(error);
d.setContentView(tv);
d.show();
}finally{
}
break;
case R.id.afficherbutton:
Intent i = new Intent("android.intent.action.SQLVIEW");
startActivity(i);
break;
}
}
}
`
view #3 :
package com.jeux;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class SQLview extends Activity {
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.afficherlayout);
TextView tv = (TextView) findViewById(R.id.affichertextview);
Database2 info = new Database2(this);
info.open();
String data = info.getCategorieData();
info.close();
tv.setText(data);
}
}
When you close/re-open your virtual device, does it look like it's doing a full bootup every time (Shiny Android word, swipe to unlock screen, etc)? If so, it could be re-initializing everything everytime you run your application. If so, you're application and database are no longer on the device, so the data wouldn't persist.
When you create your emulator are you checking the "Snapshot" option? I believe that saves the state of your device into a file so that when you close/re-open your emulator, it's able to resume from the same point.