SQLite Database for Commenting function - sqlite

There is no errors with the codes. However, I am trying to build a comment section where the name and comments will be saved in the database(SQLite).
1) It does not stay in the Textview after i come back from another page.
2) Example, i commented once, and the comment shows. I comment again, it actually re-displays the previous and the new comments together.
public class BuyerHome extends AppCompatActivity {
DatabaseHelper2 myDB;
//EditText name,comment;
EditText nameIn;
EditText commentIn;
TextView viewComment;
Button postComment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_buyer_home);
nameIn = (EditText) findViewById(R.id.nameInput);
commentIn = (EditText) findViewById(R.id.commentText);
viewComment = (TextView) findViewById(R.id.viewCommentText);
myDB = new DatabaseHelper2(this);
postComment = (Button) findViewById(R.id.buttonComment);
postComment.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String name = nameIn.getText().toString();
String comment = commentIn.getText().toString();
if(nameIn.length()!=0 && commentIn.length()!=0){
AddData(name,comment);
nameIn.setText("");
commentIn.setText("");
}else{
Toast.makeText(BuyerHome.this, "Insert fields",
Toast.LENGTH_SHORT).show();
}
Cursor data = myDB.getContents();
if(data.getCount()==0){
Toast.makeText(BuyerHome.this, "Database empty",
Toast.LENGTH_SHORT).show();
}
else{
while(data.moveToNext()){
viewComment.append(data.getString(1));
viewComment.append(data.getString(2)+"\n");
}
}
//viewComment.append("\n"+name+":"+comment);
}
});
}
public void AddData(String name,String comment){
boolean insertData = myDB.insertData(name,comment);
if(insertData==true){
Toast.makeText(this, "Success", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this, "Fail", Toast.LENGTH_SHORT).show();
}
}
}
1) I would like the past comments to be there as long as the page is available.
2) I would like only the latest comment to be displayed(on top of the past comments), and not the past comments(re-displaying again) with the new comment.

I'd suggest perhaps using a second view for the older comments. You would also need a means of determining the latest as opposed to older comments.
In the example below the second view is a Listview (this would enable you to click on a specific message, perhaps quoting it, perhaps extracting the name).
The example caters for two ways of determining the last posted comment via the id column (note _id is actually used as the column name as this then allows a Cursor Adapter to be used for the ListView which can be advantageous).
To facilitate showing the comments after returning the onResume method has been overridden inn the activity.
here's the code :-
DatabaseHelper2.java
public class DatabaseHelper2 extends SQLiteOpenHelper {
private final static String DBNAME = "buyerdb";
private final static int DBVERSION = 1;
public final static String TBL_COMMENT = "comment";
public final static String COL_COMMENT_ID = BaseColumns._ID;
public final static String COL_COMMENT_NAME = "name";
public final static String COL_COMMENT_COMMENT = "comment";
public final static String COl_COMMENT_TIMESTAMP = "timestamp";
private String crt_tbl_comment = "CREATE TABLE IF NOT EXISTS " + TBL_COMMENT + "(" +
COL_COMMENT_ID + " INTEGER PRIMARY KEY, " +
COL_COMMENT_NAME + " TEXT," +
COL_COMMENT_COMMENT + " TEXT, " +
COl_COMMENT_TIMESTAMP + " TEXT DEFAULT CURRENT_TIMESTAMP" +
")";
SQLiteDatabase mDB;
public DatabaseHelper2(Context context) {
super(context, DBNAME, null, DBVERSION);
mDB = this.getWritableDatabase();
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(crt_tbl_comment);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
public long addData(String name, String comment) {
ContentValues cv = new ContentValues();
cv.put(COL_COMMENT_NAME,name);
cv.put(COL_COMMENT_COMMENT,comment);
return mDB.insert(TBL_COMMENT,null,cv);
}
public Cursor getLatestComment() {
return mDB.query(TBL_COMMENT,null,null,null,null,null,COl_COMMENT_TIMESTAMP + " DESC","1");
}
public Cursor getAllButLatestComment() {
String whereclause = COL_COMMENT_ID + " < (SELECT max(" +
COL_COMMENT_ID +
") FROM " + TBL_COMMENT +
")";
return mDB.query(TBL_COMMENT,null,whereclause,null,null,null,COl_COMMENT_TIMESTAMP + " DESC");
}
}
Note that the timestamp column hasn't been utilised but could be, as when a row (a comment) is added the current timestamp will automatically be used to set the column.
Note the getLatestComment and the getAllButLatestComment methods return a Cursor with the appropriate rows.
BuyerHome.java
public class BuyerHome extends AppCompatActivity {
EditText nameIn;
EditText commentIn;
TextView viewComment;
Button postComment;
ListView olderComments; //<<<<<<<<<< Added (Listview needs to be added to the layout)
DatabaseHelper2 myDB;
Cursor oldermessages, latestmessage; //<<<<<<<<<< Added
SimpleCursorAdapter sma; //<<<<<<<<<< Added adapter for the ListView
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_buyer_home);
nameIn = (EditText) findViewById(R.id.nameInput);
commentIn = (EditText) findViewById(R.id.commentText);
viewComment = (TextView) findViewById(R.id.viewCommentText);
olderComments = (ListView) findViewById(R.id.olderComments); //<<<<<<<<<< Added
myDB = new DatabaseHelper2(this);
postComment = (Button) findViewById(R.id.buttonComment);
postComment.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String name = nameIn.getText().toString();
String comment = commentIn.getText().toString();
if (nameIn.length() != 0 && commentIn.length() != 0) {
myDB.addData(name, comment); //<<<<<<<<<< method starts changed lower case a
nameIn.setText("");
commentIn.setText("");
} else {
Toast.makeText(BuyerHome.this, "Insert fields",
Toast.LENGTH_SHORT).show();
}
displayComments(); //<<<<<<<<<< added to refresh the display according to the latest comments
}
});
}
//<<<<<<<<<< ADDED so will display comments whenever called including returning from another activity
#Override
protected void onResume() {
super.onResume();
displayComments();
}
//<<<<<<<<<< ADDED entire method for displaying comments
private void displayComments() {
latestmessage = myDB.getLatestComment();
if (latestmessage.moveToFirst()) {
viewComment.setText(latestmessage.getString(latestmessage.getColumnIndex(DatabaseHelper2.COL_COMMENT_NAME)) +
" said :- " +
latestmessage.getString(latestmessage.getColumnIndex(DatabaseHelper2.COL_COMMENT_COMMENT)));
} else {
viewComment.setText("");
}
oldermessages = myDB.getAllButLatestComment();
if (sma == null) {
sma = new SimpleCursorAdapter(
this,
android.R.layout.simple_list_item_2,oldermessages,
new String[]{DatabaseHelper2.COL_COMMENT_NAME,DatabaseHelper2.COL_COMMENT_COMMENT},
new int[]{android.R.id.text1,android.R.id.text2},
0
);
olderComments.setAdapter(sma);
} else {
sma.swapCursor(oldermessages);
}
}
}
Result
The highlighted line is the latest comment.
The latest comment is followed by the list of all other comments.

Related

Xaramin form -calling variable from other .cs file

I am doing a quiz game in Xaramin. forms. and for the score function. if the user got a correct answer, I want the score will add 1.but in my case even the give the correct answer, the score is not adding.
I am also trying to bind to the "score" variable to a label. I want to know if i put a correct code or not.
Button
private void submit_Clicked(object sender, EventArgs e)
{
string answer = this.answer.Text;
string canswer = "correct";
if (answer != null)
{
string ranswer = answer.Replace(" ", string.Empty);
if (ranswer.ToLower() == canswer)
{
DisplayAlert("GoodJob", "You got the correct answer", "OK");
bindingModel b = new bindingModel();
b.score++;
(sender as Button).IsEnabled = false;
}
else
{
DisplayAlert("Unfortunately", "Your answer is wrong", "OK");
(sender as Button).IsEnabled = false;
}
}
}
ViewModel
public class bindingModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int displayScore => Score;
public int score = 0;
void OnPropertyChanged(int score)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(score.ToString()));
}
public int Score
{
get => score;
set
{
if (score != value)
{
score = value;
OnPropertyChanged(score);
}
}
}
}
Model
<Label Text="{Binding Score}"/>
in your page constructor, keep a reference to your VM
bindingModel VM;
// this is your constructor, the name will match your page name
public MyPage()
{
InitializeComponent();
this.BindingContext = VM = new bindingModel();
...
}
then in your event handler, you do NOT need to create a new bindingModel
// update the Count on the VM
VM.Count++;
Answer
There's two things broken here:
You are re-initializing your ViewModel instead of referencing the same instance
You are passing the wrong value into PropertyChangedEventArgs
1. Referencing the View Model
You are re-initializing the ViewModel every time by calling bindingModel b = new bindingModel();
Lets initialize the ViewModel once, store it as a field, set it as the BindingContext for our ContentPage, and reference that field in submit_Clicked
public partial class QuizPage : ContentPage
{
readonly bindingModel _bindingModel;
public QuizPage()
{
_bindingModel = new bindingModel();
BindingContext = _bindingModel;
}
private async void submit_Clicked(object sender, EventArgs e)
{
string answer = this.answer.Text;
string canswer = "correct";
Button button = (Button)sender;
if (answer != null)
{
string ranswer = answer.Replace(" ", string.Empty);
if (ranswer.ToLower() == canswer)
{
await DisplayAlert("GoodJob", "You got the correct answer", "OK");
_bindingModel.score++;
button.IsEnabled = false;
}
else
{
await DisplayAlert("Unfortunately", "Your answer is wrong", "OK");
button.IsEnabled = false;
}
}
}
}
2. PropertyChangedEventArgs
You need to pass in the name of the property to PropertyChangedEventArgs.
They way PropertyChanged works is that it announces the name of the property that has changed. In this case, it needs to broadcast that the Score property has changed.
Let's use nameof(Score) to pass in the string "Score" to PropertyChangedEventArgs:
void OnScorePropertyChanged()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(displayScore)));
}
public int Score
{
get => score;
set
{
if (score != value)
{
score = value;
OnScorePropertyChanged();
}
}
}

make a search in recylerview

I want to make a search on recyclerview, but the data appears if the phone is turned off and then turned on again, why is that?
the example photo
the app
When i'm search the recylerview is blank
but when the phones turned off and then turn on again, the data is appear
here's the code in fragment class
//search
editText = view.findViewById(R.id.searchServant);
searchButton = view.findViewById(R.id.button2);
searchButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String s = editText.getText().toString();
firebaseSearch(s);
}
});
//RecylerviewDatabaseServant
recyclerViewdatabase = view.findViewById(R.id.recyclerViewServant);
mManager = new LinearLayoutManager(getContext());
recyclerViewdatabase.setLayoutManager(mManager);
FirebaseRecyclerOptions<Servant> options =
new FirebaseRecyclerOptions.Builder<Servant>().setQuery(FirebaseDatabase.getInstance().getReference().child("Servant")
, Servant.class)
.build();
servantAdapter = new ServantAdapter(options);
recyclerViewdatabase.setAdapter(servantAdapter);
//End
here the function firebase search
private void firebaseSearch(String s) {
FirebaseRecyclerOptions<Servant> options = new FirebaseRecyclerOptions.Builder<Servant>().setQuery(FirebaseDatabase.getInstance()
.getReference().child("Servant").orderByChild("name").startAt(s).endAt(s + "\uf8ff"), Servant.class).build();
servantAdapter = new ServantAdapter(options);
recyclerViewdatabase.setAdapter(servantAdapter);
}
Try add this line in You search query
private void firebaseSearch(String s) {
FirebaseRecyclerOptions<Servant> options = new FirebaseRecyclerOptions.Builder<Servant>().setQuery(FirebaseDatabase.getInstance()
.getReference().child("Servant").orderByChild("name").startAt(s).endAt(s + "\uf8ff"), Servant.class).build();
servantAdapter = new ServantAdapter(options);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager({**context**}));
recyclerViewdatabase.setAdapter(servantAdapter);
servantAdapter.startListening();
}

I am trying to implement NotificationChannel and WorkManager but somehow its not working and am not seeing anything Wrong

I am trying to implement a feature where you choose a date and time and the notification pops up on your phone. so after writing some code its still not working but everything seems fine
Activity code
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
public void onClick(View view) {
Calendar customCalendar = GregorianCalendar.getInstance();
DatePicker dp = findViewById(R.id.date_picker);
TimePicker picker = findViewById(R.id.time_picker);
customCalendar.set(
dp.getYear(), dp.getMonth(), dp.getDayOfMonth(), picker.getHour(), picker.getMinute(), 0);
long customTime = customCalendar.getTimeInMillis();
SimpleDateFormat sdf = new SimpleDateFormat(getString(R.string.notification_schedule_pattern), Locale.getDefault());
long currentTime = System.currentTimeMillis();
Log.d("time", "cistomTime " + customTime);
Log.d("time", "cistomTime " + currentTime);
if (customTime > currentTime) {
Data data = new Data.Builder().putInt(NOTIFICATION_ID, 0).build();
int delay = (int) (customTime - currentTime);
scheduleNotification(delay, data);
String titleNotificationSchedule = getString(R.string.notification_schedule_title);
Snackbar.make(
view,
titleNotificationSchedule + sdf
.format(customCalendar.getTime()),
LENGTH_LONG).show();
// Snackbar.make(coordinatorLayout, "Reminder set", LENGTH_LONG)
// .setAction("Action", null).show();
} else {
String errorNotificationSchedule = "Error occured";
Snackbar.make(coordinatorLayout, errorNotificationSchedule, LENGTH_LONG).show();
}
}
});
}
private void scheduleNotification(long delay, Data data) {
OneTimeWorkRequest notificationWork = new OneTimeWorkRequest.Builder(NotifyWork.class)
.setInitialDelay(delay, MILLISECONDS).setInputData(data).build();
WorkManager instanceWorkManager = WorkManager.getInstance(getApplicationContext());
instanceWorkManager.beginUniqueWork(NOTIFICATION_WORK, REPLACE, notificationWork).enqueue();
}
Worker class
public class NotifyWork extends Worker {
public static final String NOTIFICATION_ID = "notification_id";
public static final String NOTIFICATION_NAME = "Remember";
public static final String NOTIFICATION_CHANNEL = "Reminder_Channel";
public static final String NOTIFICATION_WORK = "Notification_Work";
public NotifyWork(#NonNull Context context, #NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
#NonNull
#Override
public Result doWork() {
int id = getInputData().getInt(NOTIFICATION_ID, 0);
sendNotification(id);
return Result.success();
}
private void sendNotification(int id) {
NotificationManager notificationManager = (NotificationManager) getApplicationContext()
.getSystemService(Context.NOTIFICATION_SERVICE);
Bitmap bitmap = BitmapFactory.decodeResource(getApplicationContext().getResources(), R.drawable.ic_done_white_24dp);
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.putExtra(NOTIFICATION_ID, id);
String titleNotification = "Reminder";
String subtitleNotification = "Time To WakeUp";
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notification = new NotificationCompat.Builder(getApplicationContext(), NOTIFICATION_CHANNEL)
.setLargeIcon(bitmap).setContentTitle(titleNotification)
.setContentText(subtitleNotification).setDefaults(IMPORTANCE_DEFAULT).setSound(getDefaultUri(TYPE_NOTIFICATION))
.setContentIntent(pendingIntent).setAutoCancel(true);
notification.setPriority(IMPORTANCE_MAX);
notificationManager.notify(id, notification.build());
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
Uri ringtoneManager = getDefaultUri(TYPE_NOTIFICATION);
AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE)
.setContentType(CONTENT_TYPE_SONIFICATION).build();
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL, NOTIFICATION_NAME, NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights(true);
channel.setLightColor(RED);
channel.enableVibration(true);
channel.setSound(ringtoneManager, audioAttributes);
notificationManager.createNotificationChannel(channel);
}
}
I have a DatePicker and TimePicker, when you select date and time and click on the FAB button, you get notified at that particular time
somehow changing .setLargeIcon to .setSmallIcon and referencing the image directly without converting to bitmap eg .setSmallIcon(R.drawable.ic_done_white_24dp) solved the issue

Firebase Query not being updated

I want my firebase database link to be updated depending on what the user keys in inside the searchview but the link is not updated unless I open another activity and jump back to it.I have attacked my code in the bottom. So how do I refresh it automatically ?
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener()
{
#Override
public boolean onQueryTextSubmit(String query) {
query = sv.getQuery().toString();
Toast.makeText(MainMenu.this,query, Toast.LENGTH_SHORT).show();
makeItem();
return true;
}
#Override
public boolean onQueryTextChange(String newText) {
return false;
}
});
public void makeItem ()
{
lv = findViewById(R.id.listView);
db = FirebaseDatabase.getInstance().getReferenceFromUrl("https://vsem-inventory.firebaseio.com/ItemList").orderByChild("ProductName").startAt(query).endAt(query+"\uf8ff");
FirebaseListOptions<ItemObject> options = new FirebaseListOptions.Builder<ItemObject>()
.setLayout(R.layout.content_main_menu_list)
.setQuery(db,ItemObject.class)
.build();
mAdapter = new FirebaseListAdapter<ItemObject>(options) {
#Override
protected void populateView(#NonNull View v, #NonNull ItemObject model, int position) {
final TextView tvAmount = v.findViewById(R.id.amount);
final TextView tvName = v.findViewById(R.id.name);
final TextView tvSerial = v.findViewById(R.id.serialNo);
final TextView tvSupplier = v.findViewById(R.id.supplierName);
final ImageView more = v.findViewById(R.id.more);
ImageView statusimg = v.findViewById(R.id.status);
Drawable paidIcon = v.getResources().getDrawable(R.drawable.succes);
Drawable lateIcon = v.getResources().getDrawable(R.drawable.late);
tvName.setText(model.getProductName());
tvSerial.setText(model.getSerialNo());
tvAmount.setText(model.getQuantity());
tvSupplier.setText(model.getModel());
final String Remarks = model.getRemarks();
final String cat = model.getCategory();
if(model.getQuantity().equals("0"))
statusimg.setImageDrawable(lateIcon);
else
statusimg.setImageDrawable(paidIcon);
more.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v) {
String serialNo = tvSerial.getText().toString();
String itemName = tvName.getText().toString();
String quan = tvAmount.getText().toString();
String supplier = tvSupplier.getText().toString();
showMenu(itemName,more,serialNo,quan,supplier,cat,Remarks);
}
});
}
};
lv.setAdapter(mAdapter);
}
The standard way is to call notifyDataSetChanged() after setting your adapter to your list view
Notifies the attached observers that the underlying data has been changed and any View reflecting the data set should refresh itself.
mAdapter.notifyDataSetChanged();
Although I have seen some situations where only using this does not work and must be followed by these 2 commands.
lv.invalidateViews();
lv.scrollBy(0, 0);
And if all else comes to fail falling back on destroying and redrawing the list view might be your only viable option.
lv.destroyDrawingCache();
lv.setVisibility(ListView.INVISIBLE);
lv.setVisibility(ListView.VISIBLE);
EDIT : After looking at it a while more I just noticed you're missing listeners for your firebase. I assume you already have them somewhere as you already have the list but failing your refresh functions, what you can try is restarting the listeners whenever you're done with a query.
lv.setAdapter(mAdapter);
mAdapter.stopListening();
mAdapter.startListening();

Database update without data loss

there is a table with 3 columns: id, title, poem, favorite.
So, the favorite is written 1 or 0, the default is 0, it changes if the user adds a line to the Favorites.
The problem is that if I update the database (add more rows), all Favorites will disappear from the user. How to transfer the Favorites from the old database to the new one in the onUpgrade method?
DbHelper Class Code
public class PoemsDbHelper extends SQLiteOpenHelper {
private static String DB_NAME = "brodsky.db";
private static String DB_PATH = "";
private static final int DB_VERSION = 3;
private SQLiteDatabase db;
private final Context context;
private boolean needUpdate = false;
public PoemsDbHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
if (android.os.Build.VERSION.SDK_INT >= 17)
DB_PATH = context.getApplicationInfo().dataDir + "/databases/";
else
DB_PATH = "/data/data/" + context.getPackageName() + "/databases/";
this.context = context;
copyDataBase();
this.getReadableDatabase();
}
public void updateDataBase() throws IOException {
if (needUpdate) {
File dbFile = new File(DB_PATH + DB_NAME);
if (dbFile.exists())
dbFile.delete();
copyDataBase();
needUpdate = false;
}
}
private boolean checkDataBase() {
File dbFile = new File(DB_PATH + DB_NAME);
return dbFile.exists();
}
private void copyDataBase() {
if (!checkDataBase()) {
this.getReadableDatabase();
this.close();
try {
copyDBFile();
} catch (IOException mIOException) {
throw new Error("ErrorCopyingDataBase");
}
}
}
private void copyDBFile() throws IOException {
InputStream input = context.getAssets().open(DB_NAME);
//InputStream input = context.getResources().openRawResource(R.raw.info);
OutputStream output = new FileOutputStream(DB_PATH + DB_NAME);
byte[] buffer = new byte[1024];
int length;
while ((length = input.read(buffer)) > 0)
output.write(buffer, 0, length);
output.flush();
output.close();
input.close();
}
public boolean openDataBase() throws SQLException {
db = SQLiteDatabase.openDatabase(DB_PATH + DB_NAME, null, SQLiteDatabase.CREATE_IF_NECESSARY);
return db != null;
}
#Override
public synchronized void close() {
if (db != null)
db.close();
super.close();
}
#Override
public void onCreate(SQLiteDatabase db) {
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (newVersion > oldVersion)
needUpdate = true;
}
}
In this case it seems you are overwriting your "old" database with a new, clean, empty database. I suggest trying copying the old database file to a temporary backup, overwrite old database file with new, clean, empty database file.
Then you must define an upgrade strategy, i.e. how are fields moved from v. 1 to v. 2. What happens if you removed a field? What happens if you added a new field? Has some of your data been restructured? This is an exercise that is best sketched out on paper.
You can now attach the old, backed up database to the new, clean database (see SQL command ATTACH), and insert new records into the new database from the old, following the strategy you sketched out above.
If anything fails, you can always revert to your backup file and notify the user, instead of loosing all their data.

Resources