· 4 years ago · Dec 15, 2020, 11:40 AM
1package com.joythis.android.myphonecaller;
2
3import android.content.ContentValues;
4import android.content.Context;
5import android.database.Cursor;
6import android.database.sqlite.SQLiteDatabase;
7import android.database.sqlite.SQLiteOpenHelper;
8import android.util.Log;
9
10import androidx.annotation.Nullable;
11
12import java.util.ArrayList;
13
14/*
15for the non-volatile representation of "contacts"
16following the framework, regarding inheritance from
17SQLiteOpenHelper
18 */
19
20//1 - assume the inheritance
21public class ContactDB extends SQLiteOpenHelper{
22
23 public final static String DB_NAME = "contacts.db";
24 public final static int DB_VERSION = 5;
25
26 //create constants for all table and column names
27 public final static String TABLE_CONTACTS = "tContacts";
28 public final static String COL_ID = "_id";
29 public final static String COL_NAME = "cName";
30 public final static String COL_NUMBER = "cNumber";
31
32 /*
33 CREATE TABLE IF NOT EXISTS tContacts(
34 _id INTEGER PRIMARY KEY AUTOINCREMENT,
35 cName TEXT NOT NULL,
36 cNumber TEXT NOT NULL
37 );
38 */
39 public final static String CREATE_TABLE_CONTACTS =
40 "CREATE TABLE IF NOT EXISTS "+TABLE_CONTACTS+"(\n"+
41 COL_ID+" INTEGER PRIMARY KEY NOT NULL, \n"+
42 COL_NAME+" TEXT NOT NULL, \n"+
43 COL_NUMBER+" TEXT NOT NULL\n"+
44 ");";
45
46 /*
47 DROP TABLE IF EXISTS tContacts;
48 */
49 public final static String DROP_TABLE_CONTACTS =
50 "DROP TABLE IF EXISTS "+TABLE_CONTACTS+";";
51
52 public ContactDB(
53 Context pContext
54 ){
55 super(
56 pContext,
57 DB_NAME,
58 null,
59 DB_VERSION
60 );
61 }//ContactDB
62
63 public ContactDB(
64 @Nullable Context context,
65 @Nullable String name,
66 @Nullable SQLiteDatabase.CursorFactory factory,
67 int version
68 ){
69 super(context, name, factory, version);
70 }//ContactDB
71
72 void installDB(
73 SQLiteDatabase pDB
74 ){
75 if (pDB!=null){
76 try{
77 pDB.execSQL(
78 CREATE_TABLE_CONTACTS
79 );
80 }//try
81 catch (Exception e){
82 Log.e(
83 getClass().getName(),
84 e.toString()
85 );
86 }//catch
87 }//if
88 }//installDB
89
90 /*
91 will be AUTOMATICALLY called upon the first need to
92 use the database; e.g. upon the first "insert", or the
93 first "select".
94 The developer will NEVER, EVER, call this method directly.
95 */
96 //2
97 @Override
98 public void onCreate(SQLiteDatabase db) {
99 //perform database installation here
100 installDB(db);
101 }//onCreate
102
103 /*
104 will be AUTOMATICALLY called
105 */
106 //2
107 @Override
108 public void onUpgrade(
109 SQLiteDatabase db,
110 int oldVersion,
111 int newVersion
112 )
113 {
114 if (db!=null){
115 //upgrade/change the database infrastructure here
116 if (newVersion > oldVersion){
117 //destroy the current infrastructure7
118 //TODO: backup of the previous data
119 try {
120 db.execSQL(DROP_TABLE_CONTACTS);
121 }//try
122 catch(Exception e){
123 Log.e(
124 getClass().getName(),
125 e.toString()
126 );
127 }//catch
128 installDB(db); //rebuild using the new version of the infrastructure
129 }//if
130 }//if
131 }//onUpgrade
132
133 //insertContact
134 /*
135 will return the "line" where the record is inserted
136 */
137 public final static String KEY_NAME = "KEY_NAME";
138 public final static String KEY_NUMBER = "KEY_NUMBER";
139 public final static long DOES_NOT_EXIST = -1;
140 public long insertContact (Contact pC)
141 {
142 long idOfContact = this.idOfContact(pC);
143 boolean bWillBeNewContact = idOfContact==DOES_NOT_EXIST;
144
145 if (bWillBeNewContact){
146 SQLiteDatabase db = this.getWritableDatabase();
147 if (db!=null){
148 ContentValues pairsKeyValue = new ContentValues();
149
150 pairsKeyValue.put(
151 COL_NAME,
152 pC.getName()
153 ); //contact name
154 pairsKeyValue.put(
155 COL_NUMBER,
156 pC.getNumber()
157 ); //contact number
158
159 //-1 on failure
160 long iRecordNumber =
161 db.insert(
162 TABLE_CONTACTS,
163 null,
164 pairsKeyValue
165 );
166
167 db.close();
168
169 return iRecordNumber;
170 }//if
171
172 return -1;
173 }
174 return idOfContact;
175 }//insertContact
176
177 /*
178 public long insertContact(
179 String pStrName, String pStrNumber
180 ){
181
182 }//insertContact
183
184 */
185
186 public final static String SELECT_ALL_CONTACTS_BY_NAME_ASC =
187 "SELECT * FROM "+TABLE_CONTACTS+" ORDER BY "+COL_NAME+" ASC;";
188 public final static String SELECT_ALL_CONTACTS_BY_NAME_DESC =
189 "SELECT * FROM "+TABLE_CONTACTS+" ORDER BY "+COL_NAME+" DESC;";
190
191 public final static String SELECT_ALL_CONTACTS_BY_NUMBER_ASC =
192 "SELECT * FROM "+TABLE_CONTACTS+" ORDER BY "+COL_NUMBER+" ASC;";
193 public final static String SELECT_ALL_CONTACTS_BY_NUMBER_DESC =
194 "SELECT * FROM "+TABLE_CONTACTS+" ORDER BY "+COL_NUMBER+" DESC;";
195
196 public final static String SELECT_ALL_CONTACTS_BY_INSERT_DATE_ASC =
197 "SELECT * FROM "+TABLE_CONTACTS+" ORDER BY "+COL_ID+" ASC;";
198 public final static String SELECT_ALL_CONTACTS_BY_INSERT_DATE_DESC =
199 "SELECT * FROM "+TABLE_CONTACTS+" ORDER BY "+COL_ID+" DESC;";
200
201 public ArrayList<Contact> selectAllContactsByNameASC(){
202 return selectAllByQuery(SELECT_ALL_CONTACTS_BY_NAME_ASC);
203 }//selectAllContactsByNameASC
204
205 public ArrayList<Contact> selectAllContactsByNameDESC(){
206 return selectAllByQuery(SELECT_ALL_CONTACTS_BY_NAME_DESC);
207 }//selectAllContactsByNameASC
208
209 public ArrayList<Contact> selectAllContactsByNumberASC(){
210 return selectAllByQuery(SELECT_ALL_CONTACTS_BY_NUMBER_ASC);
211 }//selectAllContactsByNumberASC
212
213 public ArrayList<Contact> selectAllContactsByNumberDESC(){
214 return selectAllByQuery(SELECT_ALL_CONTACTS_BY_NUMBER_DESC);
215 }//selectAllContactsByNumberASC
216
217 public ArrayList<Contact> selectAllContactsByInsertDateASC(){
218 return selectAllByQuery(SELECT_ALL_CONTACTS_BY_INSERT_DATE_ASC);
219 }//selectAllContactsByNameASC
220
221 public ArrayList<Contact> selectAllContactsByInsertDateDESC(){
222 return selectAllByQuery(SELECT_ALL_CONTACTS_BY_INSERT_DATE_DESC);
223 }//selectAllContactsByNameASC
224
225 private ArrayList<Contact> selectAllByQuery(
226 String pSelectQuery
227 ){
228 ArrayList<Contact> aRet = new ArrayList<>();
229 boolean bCheck = pSelectQuery.length()>0;
230 //bCheck = !pSelectQuery.isEmpty();
231
232 if (bCheck){
233 try {
234 SQLiteDatabase db = getReadableDatabase();
235 if (db != null) {
236 Cursor cursor =
237 db.rawQuery(
238 pSelectQuery,
239 null
240 );
241
242 if (cursor != null) {
243 cursor.moveToFirst();
244 while (!cursor.isAfterLast()) {
245 String strName =
246 cursor.getString(
247 cursor.getColumnIndex(COL_NAME)
248 //which column INDEX?
249 );
250 String strNumber =
251 cursor.getString(
252 cursor.getColumnIndex(COL_NUMBER)
253 //which column INDEX?
254 );
255
256 Contact c = new Contact(strName, strNumber);
257 aRet.add(c);
258
259 cursor.moveToNext();
260 }//while
261 db.close();
262 }//if
263 }//if can read the database
264 }//try
265 catch(Exception e){
266 Log.e(
267 this.getClass().getName(), //log's category
268 e.toString() //message logged (will be visible in LogCat)
269 );
270 }//catch
271 }//if got a non empty query
272
273 return aRet;
274 }//selectAll
275}//ContactDB
276
277
278**
279
280package com.joythis.android.myphonecaller;
281
282import androidx.annotation.NonNull;
283import androidx.appcompat.app.AppCompatActivity;
284
285import android.Manifest;
286import android.content.Context;
287import android.content.pm.PackageManager;
288import android.os.Bundle;
289import android.view.Menu;
290import android.view.MenuInflater;
291import android.view.MenuItem;
292import android.view.View;
293import android.widget.AdapterView;
294import android.widget.ArrayAdapter;
295import android.widget.Button;
296import android.widget.EditText;
297import android.widget.ListView;
298
299import java.util.ArrayList;
300import java.util.Map;
301
302public class MainActivity extends AppCompatActivity {
303 //const to represent the necessary runtime permissions
304 public final static String[] NECESSARY_PERMISSIONS =
305 {
306 Manifest.permission.CALL_PHONE
307 };
308
309 public final int
310 CALL_ME_ON_THIS_CODE_WHEN_THE_USER_REPLIES_TO_THE_REQ = 321;
311
312 //data members
313 Context mContext;
314 ContactDB mDB;
315 AmUtil mUtil;
316 ArrayList<Contact> mAlContacts; //runtime representation of the contacts managed by the app
317
318 EditText mEtName, mEtNumber;
319 Button mBtnInsertContact;
320 ListView mLvContacts;
321 ArrayAdapter<Contact> mAd; //"default" adapter, in use until 2020-12-10
322 ContactAdapter mAd2; //2020-12-10, custom adapter
323
324 ListView.OnItemLongClickListener mLvLongClickHandler =
325 new ListView.OnItemLongClickListener() {
326 @Override
327 public boolean onItemLongClick
328 (AdapterView<?> parent, //the ListView
329 View view, //the particular item with which the user is interacting with
330 int position, //the index of the item in the corresponding data
331 long id
332 )
333 {
334 Contact c = mAlContacts.get(position);
335 String strNumber = c.getNumber();
336
337 mUtil.fb("Will phone to "+strNumber);
338 mUtil.phoneTo(strNumber);
339
340 //return false; //the chain of events will keep going on
341 return true; //the event was fully consumed
342 }//onItemLongClick
343 };//mLvLongClickHandler
344
345 Button.OnClickListener mButtonClickHandler =
346 new Button.OnClickListener() {
347 @Override
348 public void onClick(View v) {
349 switch(v.getId()){
350 case R.id.idBtnInsertContact:
351 actionInsertContact();
352 break;
353
354 case R.id.idBtnCall:
355 Button btnThatWasClicked = (Button)v;
356 String strContactNumber = btnThatWasClicked.getText().toString();
357 mUtil.fb("About to call "+strContactNumber);
358 mUtil.phoneTo(strContactNumber);
359 break;
360 }//switch
361 }//onClick
362 };//mButtonClickHandler
363
364 void actionInsertContact(){
365 String strName = mEtName.getText().toString().trim();
366 String strNumber = mEtNumber.getText().toString().trim();
367
368 boolean bCheck = strName.length()>0 && strNumber.length()>0;
369 if (bCheck){
370 Contact c = new Contact(strName, strNumber);
371 long iWhereInserted = mDB.insertContact(c);
372 //mAlContacts.add(c);
373 ArrayList<Contact> alAfterInsert = mDB.selectAllContactsByInsertDateDESC();
374 syncContactsDataWithTheirPresentation(alAfterInsert);
375 }//
376 else{
377 mUtil.fb("Name and Number can NOT be empty!");
378 }
379 }//actionInsertContact
380
381 @Override
382 protected void onCreate(Bundle savedInstanceState) {
383 super.onCreate(savedInstanceState);
384 setContentView(R.layout.insert_contact_rl);
385
386 init();
387 }//onCreate
388
389 void init(){
390 mContext = this;
391 mDB = new ContactDB(this);
392 mUtil = new AmUtil(this);
393 mAlContacts = new ArrayList<>();
394
395 mEtName = findViewById(R.id.idEtName);
396 mEtNumber = findViewById(R.id.idEtNumber);
397 mBtnInsertContact = findViewById(R.id.idBtnInsertContact);
398 mBtnInsertContact.setOnClickListener(
399 mButtonClickHandler
400 );
401
402 mLvContacts = findViewById(R.id.idLvContacts);
403
404 mAd = new ArrayAdapter<>(
405 mContext,
406 android.R.layout.simple_list_item_1, //the object for viewing each list item
407 mAlContacts //the data (at a specific memory address)
408 );
409
410 //speedy
411 mAd2 = new ContactAdapter(
412 mContext,
413 R.layout.contact_ll,
414 mAlContacts,
415 mButtonClickHandler
416 );
417
418 //mLvContacts.setAdapter(mAd);
419 mLvContacts.setAdapter(mAd2);
420 mLvContacts.setOnItemLongClickListener
421 (mLvLongClickHandler);
422
423 //TODO:
424 /*
425 layout
426 op layout
427 */
428
429 ArrayList<Contact> alSortedByDateDesc = mDB.selectAllContactsByInsertDateDESC();
430 syncContactsDataWithTheirPresentation(alSortedByDateDesc);
431
432 mUtil.requestNecessaryPermissionsNotYetGranted(
433 NECESSARY_PERMISSIONS,
434 CALL_ME_ON_THIS_CODE_WHEN_THE_USER_REPLIES_TO_THE_REQ
435 );
436 }//init
437
438 /*
439 method that will automaticall called when the
440 user completes his/her answers regarding the permission(s)
441 that were requested
442 */
443 @Override
444 public void onRequestPermissionsResult(
445 int requestCode, //identified to whom the answer is destined to
446 //paralell arrays
447 @NonNull String[] permissions, //e.g. {p1, p2}
448 @NonNull int[] grantResults //e.g. {GRANTED, DENIED}
449 )
450 {
451 for (int idx=0; idx<permissions.length; idx++){
452 String strCurrentPermission = permissions[idx];
453 int iStatus = grantResults[idx];
454 boolean bDenied =
455 iStatus == PackageManager.PERMISSION_DENIED;
456 if (bDenied){
457 if (this.shouldShowRequestPermissionRationale(
458 strCurrentPermission
459 )){
460 //explain why it is important NOT to deny the permission
461 mUtil.fb(
462 "without the"+
463 strCurrentPermission +
464 " the app will not work as it should."
465 );
466 }
467 }
468 }
469
470 super.onRequestPermissionsResult(requestCode, permissions, grantResults);
471 }//onRequestPermissionsResult
472
473 /*
474 this method assigns a "menu resource file" to the
475 activity's action bar.
476 */
477 @Override
478 public boolean onCreateOptionsMenu(Menu pMenu) {
479 MenuInflater minf = this.getMenuInflater();
480 if (minf!=null){
481 minf.inflate(
482 R.menu.sorting_options_menu,//id the XML for the menu
483 pMenu //Java ref to assign to the menu
484 );
485 }//if
486
487 return super.onCreateOptionsMenu(pMenu);
488 }//onCreateOptionsMenu
489
490 /*
491 here we code what should happen upon the selection of
492 any of the menu options
493 */
494 @Override
495 public boolean onOptionsItemSelected(@NonNull MenuItem pMenuItem) {
496 switch(pMenuItem.getItemId()){
497 case R.id.idMenuItemRequestDeniedPerms:
498 mUtil.requestNecessaryPermissionsNotYetGranted(
499 NECESSARY_PERMISSIONS,
500 CALL_ME_ON_THIS_CODE_WHEN_THE_USER_REPLIES_TO_THE_REQ
501 );
502 break;
503
504 case R.id.idMenuItemSortNameAsc:
505 actionSortNameAsc();
506 break;
507 case R.id.idMenuItemSortNameDesc:
508 actionSortNameDesc();
509 break;
510 case R.id.idMenuItemSortNumberAsc:
511 actionSortNumberAsc();
512 break;
513 case R.id.idMenuItemSortNumberDesc:
514 actionSortNumberDesc();
515 break;
516 case R.id.idMenuItemSortInsertDateAsc:
517 actionSortInsertDateAsc();
518 break;
519 case R.id.idMenuItemSortInsertDateDesc:
520 actionSortInsertDateDesc();
521 break;
522 case R.id.idMenuItemCheckPermissions:
523 actionCheckPermissions();
524 break;
525 }//switch
526 return super.onOptionsItemSelected(pMenuItem);
527 }
528
529 void actionCheckPermissions(){
530 String strCurrentPermissionsStatus =
531 mUtil.permissionsStatusToString(NECESSARY_PERMISSIONS);
532
533 mUtil.fb(strCurrentPermissionsStatus);
534 }//actionCheckPermissions
535
536 void actionSortNameAsc(){
537 ArrayList<Contact> alAfterUserChoice = mDB.selectAllContactsByNameASC();
538 syncContactsDataWithTheirPresentation(alAfterUserChoice);
539 }
540
541 void actionSortNameDesc(){
542 ArrayList<Contact> alAfterUserChoice = mDB.selectAllContactsByNameDESC();
543 syncContactsDataWithTheirPresentation(alAfterUserChoice);
544 }
545
546 void actionSortNumberAsc(){
547 ArrayList<Contact> alAfterUserChoice = mDB.selectAllContactsByNumberASC();
548 syncContactsDataWithTheirPresentation(alAfterUserChoice);
549 }
550
551 void actionSortNumberDesc(){
552 ArrayList<Contact> alAfterUserChoice = mDB.selectAllContactsByNumberDESC();
553 syncContactsDataWithTheirPresentation(alAfterUserChoice);
554 }
555
556 void actionSortInsertDateAsc(){
557 ArrayList<Contact> alAfterUserChoice = mDB.selectAllContactsByInsertDateASC();
558 syncContactsDataWithTheirPresentation(alAfterUserChoice);
559 }
560
561 void actionSortInsertDateDesc(){
562 ArrayList<Contact> alAfterUserChoice = mDB.selectAllContactsByInsertDateDESC();
563 syncContactsDataWithTheirPresentation(alAfterUserChoice);
564 }
565
566 void syncContactsDataWithTheirPresentation(
567 ArrayList<Contact> pAlContacts
568 ){
569 //would be wrong!
570 //mAlContacts = pAlContacts; //would change the memory address!
571
572 if (pAlContacts!=null && pAlContacts.size()>0){
573 mAlContacts.clear(); //clears the data, but keeps the original memory address to which the Adapter was bound
574 mAlContacts.addAll(pAlContacts);
575 /*
576 for (Contact c: pAlContacts){
577 mAlContacts.add(c);
578 }//for
579 */
580 }//if
581
582 mAd2.notifyDataSetChanged();
583 }//syncContactsDataWithTheirPresentation
584}//MainActivity
585
586