1public class Lesson5Tab2DCMigrations extends Lesson5BaseFragment {
2
3 private MigrationDBv2 databaseV2;
4 private SharedPreferencesMigDB sf;
5
6 private Button insertRandomButton;
7 private Button clearTableButton;
8 private Button deleteDatabaseButton;
9
10 private ListView eventsListView;
11
12 private TextView statusTV;
13
14 private MigDatabaseState mdbState;
15
16
17 final static int DB_IMPLEMENTATION_VERSION = 2;
18 final static int TABLE_AMOUNT = 2;
19
20 public Lesson5Tab2DCMigrations() {}
21
22 @Override
23 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
24 View rootView = inflater.inflate(R.layout.lesson5_tab2_dc_migration, container, false);
25
26 insertRandomButton = rootView.findViewById(R.id.l5_t2_go_button);
27 clearTableButton = rootView.findViewById(R.id.l5_t2_clear_button);
28 deleteDatabaseButton = rootView.findViewById(R.id.l5_t2_delete_database_button);
29
30 eventsListView = rootView.findViewById(R.id.l5_t2_actions);
31
32 insertRandomButton.setOnClickListener(new View.OnClickListener() {
33 @Override
34 public void onClick(View v) {
35 insert25RND();
36 }
37 });
38
39 clearTableButton.setOnClickListener(new View.OnClickListener() {
40 @Override
41 public void onClick(View v) {
42 clearTable();
43 }
44 });
45
46 deleteDatabaseButton.setOnClickListener(new View.OnClickListener() {
47 @Override
48 public void onClick(View v) {
49 deleteDatabase();
50 }
51 });
52
53 statusTV = rootView.findViewById(R.id.l5_t2_status);
54
55
56 setUpExpandedList(
57 rootView,
58 R.id._l5_t2_expanded_panel_list,
59 R.id._l5_t2_expanded_panel_text,
60 R.string._l5_t2_expanded_text_pattern
61 );
62
63 reloadTableExpandedList();
64 reloadStatus();
65 return rootView;
66 }
67
68 public MigDatabaseState getMdbState(Context context, int implVersion, String[] tables) {
69 if(mdbState != null) return mdbState;
70 mdbState = new MigDatabaseState(implVersion, tables, context, getSf());
71 return mdbState;
72 }
73
74 public void reloadStatus() {
75 if(statusTV != null) {
76 statusTV.setText(getMdbState(getContext(), DB_IMPLEMENTATION_VERSION, new String[] {M1M1TN, M1M2TN}).toString());
77 }
78 }
79
80
81 @Override
82 public void onVisible() {
83 reloadTableExpandedList();
84 reloadStatus();
85 }
86
87 private SharedPreferencesMigDB getSf() {
88 if(sf != null) return sf;
89 sf = new SharedPreferencesMigDB(getContext());
90 return sf;
91 }
92
93 private MigrationDBv2 getDatabase() {
94 // retrieving existing database after upgrade -> downgrade would cause onUpgrade() script would be run after mapper fetching
95 databaseV2 = new MigrationDBv2(getContext());
96 return databaseV2;
97 }
98
99 private void insert25RND() {
100 new InsertRandomAsync().execute(0l);
101 }
102
103 private void clearTable() {
104 new WipeAsync().execute(0l);
105 }
106
107 private void deleteDatabase() {
108 new DeleteDatabaseAsync().execute(0l);
109 }
110
111 private void reloadTableExpandedList() {
112 new ReloadTableAsync().execute(0l);
113 }
114
115 @Override
116 protected int snackbarMessageResource() {
117 return R.string._l5_t2_snackbar_message;
118 }
119
120
121
122 // Asyncs
123
124 class ReloadTableAsync extends AsyncTask<Long, Long, List<String>> {
125
126 @Override
127 protected List<String> doInBackground(Long... params) {
128 LinkedList<String> toListView = new LinkedList<>();
129 if(getSf().isDatabaseCreated() && !getSf().isDatabaseDeletedManually() && getSf().currentMigDBVersion() == DB_IMPLEMENTATION_VERSION) {
130 KittyMapper mapper = getDatabase().getMapper(MigOneModel.class);
131 List<MigOneModel> m1Models = mapper.findAll();
132 mapper.close();
133 KittyMapper mapperT2 = getDatabase().getMapper(MigTwoModel.class);
134 List<MigTwoModel> m2Models = mapperT2.findAll();
135 if(m1Models == null) {
136 toListView.addLast(format(getContext().getString(R.string._l5_t2_m1_db), 0));
137 } else {
138 toListView.addLast(format(getContext().getString(R.string._l5_t2_m1_db), m1Models.size()));
139 Iterator<MigOneModel> mI = m1Models.iterator();
140 while (mI.hasNext()) {
141 toListView.addLast(mI.next().toString());
142 }
143 }
144 if(m2Models == null) {
145 toListView.addLast(format(getContext().getString(R.string._l5_t2_m2_db), 0));
146 } else {
147 toListView.addLast(format(getContext().getString(R.string._l5_t2_m2_db), m2Models.size()));
148 Iterator<MigTwoModel> mI = m2Models.iterator();
149 while (mI.hasNext()) {
150 toListView.addLast(mI.next().toString());
151 }
152 }
153 return toListView;
154 } else {
155 if(!getSf().isDatabaseCreated() || getSf().isDatabaseDeletedManually()) {
156 toListView.addLast(getString(R.string._l5_t2_m1_db_doesnt_exist));
157 return toListView;
158 } else {
159 toListView.addLast(format(getString(R.string._l5_t2_m1_db_has_different_version), getSf().currentMigDBVersion()));
160 return toListView;
161 }
162 }
163 }
164
165 @Override
166 protected void onPostExecute(List<String> result) {
167 int tableAmount = TABLE_AMOUNT;
168 if(getSf().isDatabaseDeletedManually() || !getSf().isDatabaseCreated() || getSf().currentMigDBVersion() != DB_IMPLEMENTATION_VERSION)
169 tableAmount = 0;
170 if(result != null) {
171 events.setAdapter(new MigAdapter(getContext(), result));
172 int recordsAmount = result.size() - TABLE_AMOUNT;
173 if(tableAmount == 0)
174 recordsAmount = 0;
175 expandedTitle.setText(format(expandeddTitlePattern, recordsAmount, tableAmount));
176 } else {
177 events.setAdapter(new MigAdapter(getContext(), new LinkedList<String>()));
178 expandedTitle.setText(format(expandeddTitlePattern, 0, tableAmount));
179 }
180 }
181 }
182
183 private static final String ERR_STRING_WIPE = "Lesson5tab2WipeDataError, see exception details!";
184
185 class WipeAsync extends AsyncTask<Long, Long, WipeAsyncResult> {
186
187 ProgressDialog dialog;
188
189 @Override
190 protected void onPreExecute() {
191 dialog = ProgressDialog.show(
192 getLessonActivity(),
193 getString(R.string._l5_t2_running_requested_operation_pg_title),
194 getString(R.string._l5_t2_running_requested_operation_pg_body)
195 );
196 dialog.setCancelable(false);
197 }
198
199 @Override
200 protected WipeAsyncResult doInBackground(Long... params) {
201 if(getSf().isDatabaseCreated() && !getSf().isDatabaseDeletedManually() && getSf().currentMigDBVersion() == DB_IMPLEMENTATION_VERSION) {
202 try {
203 KittyMapper mapper = getDatabase().getMapper(MigOneModel.class);
204 KittyMapper mapper2 = getDatabase().getMapper(MigTwoModel.class);
205 long recordsCount = mapper.countAll() + mapper2.countAll();
206 long affected = mapper.deleteAll() + mapper2.deleteAll();
207 mapper.close(); mapper2.close();
208 return new WipeAsyncResult(true, false, DB_IMPLEMENTATION_VERSION, affected, recordsCount);
209 } catch (Exception e) {
210 Log.e(MigrationDBv2.LTAG, ERR_STRING_WIPE, e);
211 if (e instanceof KittyRuntimeException) {
212 if (((KittyRuntimeException) e).getNestedException() != null) {
213 Log.e(MigrationDBv2.LTAG, ERR_STRING_WIPE, ((KittyRuntimeException) e).getNestedException());
214 }
215 }
216 return new WipeAsyncResult(true, false, DB_IMPLEMENTATION_VERSION, -1l, -1l);
217 }
218 } else {
219 return new WipeAsyncResult(
220 getSf().isDatabaseCreated(),
221 getSf().isDatabaseDeletedManually(),
222 getSf().currentMigDBVersion(),
223 -1l, -1l);
224 }
225 }
226
227 @Override
228 protected void onPostExecute(WipeAsyncResult result) {
229 dialog.cancel();
230
231 if (eventsListView != null) {
232 eventsListView.setAdapter(new BasicArrayAdapter(getContext(), new LinkedList<String>()));
233 eventsListView.setOnTouchListener(new View.OnTouchListener() {
234
235 // Setting on Touch Listener for handling the touch inside ScrollView
236 @Override
237 public boolean onTouch(View v, MotionEvent event) {
238 // Disallow the touch request for parent scroll on touch of child view
239 v.getParent().requestDisallowInterceptTouchEvent(true);
240 return false;
241 }
242 });
243
244 if(!result.isCreated) {
245 ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(getString(R.string._l5_op_not_existing));
246 } else if (result.isDeleted) {
247 ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(getString(R.string._l5_op_deleted));
248 } else if (result.dbVersion != DB_IMPLEMENTATION_VERSION) {
249 if(result.dbVersion < 1) {
250 ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_op_mig_version_is_lower), result.dbVersion, DB_IMPLEMENTATION_VERSION));
251 } else {
252 ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_op_mig_version_is_higher), result.dbVersion, DB_IMPLEMENTATION_VERSION));
253 }
254 } else if (result.recordsCount > -1 && result.affectedRows > -1) {
255 ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t2_count_to_events), result.recordsCount));
256 ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t2_deleted_to_events), result.affectedRows));
257 } else {
258 ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(getString(R.string._l5_t2_error_event));
259 }
260 ((BasicArrayAdapter) eventsListView.getAdapter()).notifyDataSetChanged();
261 reloadTableExpandedList();
262 reloadStatus();
263 }
264 }
265 }
266
267 class WipeAsyncResult {
268 boolean isCreated;
269 boolean isDeleted;
270 int dbVersion;
271 Long affectedRows;
272 Long recordsCount;
273
274 public WipeAsyncResult(boolean isCreated, boolean isDeleted, int dbVersion,
275 Long affectedRows, Long recordsCount) {
276 this.isCreated = isCreated;
277 this.isDeleted = isDeleted;
278 this.dbVersion = dbVersion;
279 this.affectedRows = affectedRows;
280 this.recordsCount = recordsCount;
281 }
282 }
283
284 static final int INSERT_AMOUNT = 25;
285 static final int INSERT_FK_AMOUNT = 10;
286
287 static final String ERR_INSERT_RND = "Lesson5tab2InsertRNDDataError, see exception details!";
288
289 class InsertRandomAsync extends AsyncTask<Long, Long, InsertRandomResults> {
290 ProgressDialog dialog;
291
292 @Override
293 protected void onPreExecute() {
294 dialog = ProgressDialog.show(
295 getLessonActivity(),
296 getString(R.string._l5_t2_running_requested_operation_pg_title),
297 getString(R.string._l5_t2_running_requested_operation_pg_body)
298 );
299 dialog.setCancelable(false);
300 }
301
302 @Override
303 protected InsertRandomResults doInBackground(Long... strings) {
304 if(getSf().currentMigDBVersion() > DB_IMPLEMENTATION_VERSION) {
305 return new InsertRandomResults(
306 null,
307 null,
308 -1l,
309 -1l,
310 -1l,
311 false,
312 getSf().currentMigDBVersion()
313 );
314 } else {
315 try {
316 KittyMapper mapper = getDatabase().getMapper(MigOneModel.class);
317 KittyMapper mapper2 = getDatabase().getMapper(MigTwoModel.class);
318 long recordsCount = mapper.countAll() + mapper2.countAll();
319 long affected = mapper.deleteAll() + mapper2.deleteAll();
320 LinkedList<MigOneModel> modelsToInsert = new LinkedList<>();
321 getSf().setDatabaseCreated(true);
322 getSf().setCurrentMigDBVersion(DB_IMPLEMENTATION_VERSION);
323 getSf().setDatabaseDeletedManually(false);
324 MigV2RandomModelFactory factory = new MigV2RandomModelFactory(getContext());
325 for (int i = 0; i < INSERT_AMOUNT; i++) {
326 MigOneModel m = factory.newM1RndModel();
327 modelsToInsert.addLast(m);
328 }
329 mapper.insertInTransaction(modelsToInsert);
330 List<MigOneModel> models = mapper.findAll();
331
332 Iterator<MigOneModel> mI = models.iterator();
333 LinkedList<String> out = new LinkedList<>();
334 while (mI.hasNext()) {
335 out.addLast(mI.next().toString());
336 }
337
338 LinkedList<MigTwoModel> models2ToInsert = new LinkedList<>();
339 for (int i = 0; i < INSERT_FK_AMOUNT; i++) {
340 MigTwoModel m = factory.newM2RndModel((ArrayList<MigOneModel>) models);
341 models2ToInsert.addLast(m);
342 }
343 mapper2.insertInTransaction(models2ToInsert);
344 List<MigTwoModel> models2 = mapper2.findAll();
345
346 LinkedList<String> out2 = new LinkedList<>();
347 Iterator<MigTwoModel> mI2 = models2.iterator();
348 while (mI2.hasNext()) {
349 out2.addLast(mI2.next().toString());
350 }
351
352 long recordsCountAfter = mapper.countAll() + mapper2.countAll();
353 mapper.close(); mapper2.close();
354 return new InsertRandomResults(out, out2, affected, recordsCount, recordsCountAfter, true, getSf().currentMigDBVersion());
355 } catch (Exception e) {
356 Log.e(MigrationDBv1.LTAG, ERR_INSERT_RND, e);
357 if (e instanceof KittyRuntimeException) {
358 if (((KittyRuntimeException) e).getNestedException() != null) {
359 Log.e(MigrationDBv1.LTAG, ERR_INSERT_RND, ((KittyRuntimeException) e).getNestedException());
360 }
361 }
362 return new InsertRandomResults(
363 null,
364 null,
365 -1l,
366 -1l,
367 -1l,
368 false,
369 getSf().currentMigDBVersion()
370 );
371 }
372 }
373 }
374
375 @Override
376 protected void onPostExecute(InsertRandomResults result) {
377 dialog.cancel();
378 if (eventsListView != null) {
379 eventsListView.setAdapter(new BasicArrayAdapter(getContext(), new LinkedList<String>()));
380 eventsListView.setOnTouchListener(new View.OnTouchListener() {
381
382 // Setting on Touch Listener for handling the touch inside ScrollView
383 @Override
384 public boolean onTouch(View v, MotionEvent event) {
385 // Disallow the touch request for parent scroll on touch of child view
386 v.getParent().requestDisallowInterceptTouchEvent(true);
387 return false;
388 }
389 });
390 if (result.operationSuccess) {
391 ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t2_count_to_events), result.modelsCountBefore));
392 ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t2_deleted_to_events), result.deletedModelsAffectedRows));
393 for (String modelString : result.modelInsertionsM1) {
394 ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t2_inserted_to_events), M1M1TN, modelString));
395 }
396 for (String modelString2 : result.modelInsertionsM2) {
397 ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t2_inserted_to_events), M1M2TN, modelString2));
398 }
399 ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t2_count_to_events), result.modelsCountAfter));
400 } else {
401 if(getSf().currentMigDBVersion() > DB_IMPLEMENTATION_VERSION) {
402 ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_op_mig_version_is_higher), result.dbVersion, DB_IMPLEMENTATION_VERSION));
403 } else {
404 ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(getString(R.string._l5_t2_error_event));
405 }
406 }
407 ((BasicArrayAdapter) eventsListView.getAdapter()).notifyDataSetChanged();
408 reloadTableExpandedList();
409 reloadStatus();
410 }
411 }
412
413
414 }
415
416 class InsertRandomResults {
417 List<String> modelInsertionsM1;
418 List<String> modelInsertionsM2;
419 long deletedModelsAffectedRows;
420 long modelsCountBefore;
421 long modelsCountAfter;
422 boolean operationSuccess;
423 int dbVersion;
424
425 public InsertRandomResults(List<String> modelInsertionsM1, List<String> modelInsertionsM2,
426 long deletedModelsAffectedRows,
427 long modelsCountBefore, long modelsCountAfter, boolean opSuccess,
428 int dbVersion) {
429 this.modelInsertionsM1 = modelInsertionsM1;
430 this.modelInsertionsM2 = modelInsertionsM2;
431 this.deletedModelsAffectedRows = deletedModelsAffectedRows;
432 this.modelsCountBefore = modelsCountBefore;
433 this.modelsCountAfter = modelsCountAfter;
434 this.operationSuccess = opSuccess;
435 this.dbVersion = dbVersion;
436 }
437 }
438
439 static final String ERR_DELETION = "Lesson5tab2DBDeleteError, see exception details!";
440
441 class DeleteDatabaseAsync extends AsyncTask<Long, Long, Integer> {
442 ProgressDialog dialog;
443
444 final int DELETED = 1;
445 final int NOT_DELETED = 2;
446 final int ERROR = 3;
447
448 @Override
449 protected void onPreExecute() {
450 dialog = ProgressDialog.show(
451 getLessonActivity(),
452 getString(R.string._l5_t2_running_requested_operation_pg_title),
453 getString(R.string._l5_t2_running_requested_operation_pg_body)
454 );
455 dialog.setCancelable(false);
456 }
457
458 @Override
459 protected Integer doInBackground(Long... strings) {
460 try {
461 boolean deleted = getDatabase().deleteDatabase();
462 getSf().setDatabaseDeletedManually(true);
463 getSf().setDatabaseCreated(false);
464 getSf().setCurrentMigDBVersion(-1);
465 if(deleted)
466 return DELETED;
467 else
468 return NOT_DELETED;
469 } catch (Exception e) {
470 Log.e(MigrationDBv2.LTAG, ERR_DELETION, e);
471 if (e instanceof KittyRuntimeException) {
472 if (((KittyRuntimeException) e).getNestedException() != null) {
473 Log.e(MigrationDBv2.LTAG, ERR_DELETION, ((KittyRuntimeException) e).getNestedException());
474 }
475 }
476 return ERROR;
477 }
478 }
479
480 @Override
481 protected void onPostExecute(Integer result) {
482 dialog.cancel();
483 if (eventsListView != null) {
484 eventsListView.setAdapter(new BasicArrayAdapter(getContext(), new LinkedList<String>()));
485 eventsListView.setOnTouchListener(new View.OnTouchListener() {
486
487 // Setting on Touch Listener for handling the touch inside ScrollView
488 @Override
489 public boolean onTouch(View v, MotionEvent event) {
490 // Disallow the touch request for parent scroll on touch of child view
491 v.getParent().requestDisallowInterceptTouchEvent(true);
492 return false;
493 }
494 });
495 switch (result) {
496 case DELETED:
497 ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t2_delete_db_success)));
498 break;
499 case NOT_DELETED:
500 ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t2_delete_db_fail)));
501 break;
502 case ERROR:
503 ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t2_error_event)));
504 break;
505 }
506 ((BasicArrayAdapter) eventsListView.getAdapter()).notifyDataSetChanged();
507 reloadTableExpandedList();
508 reloadStatus();
509 }
510 }
511 }
512
513 // Expanded list
514 MigAdapter migAdapter;
515
516 @Override
517 protected void setUpExpandedList(View rootView, int eventsId, int eventsTitleId, int eventTitleStringPattern) {
518 events = (ListView) rootView.findViewById(eventsId);
519 expandedTitle = (TextView) rootView.findViewById(eventsTitleId);
520 expandeddTitlePattern = getString(eventTitleStringPattern);
521
522 expandedTitle.setText(format(expandeddTitlePattern, 0));
523
524 if(expandedAdapter == null) {
525 migAdapter = new MigAdapter(getContext(), new LinkedList<String>());
526 }
527
528 events.setAdapter(migAdapter);
529 events.setOnTouchListener(new View.OnTouchListener() {
530
531 // Setting on Touch Listener for handling the touch inside ScrollView
532 @Override
533 public boolean onTouch(View v, MotionEvent event) {
534 // Disallow the touch request for parent scroll on touch of child view
535 v.getParent().requestDisallowInterceptTouchEvent(true);
536 return false;
537 }
538 });
539 }
540
541 // Fab menu section
542
543 @Override
544 public View.OnClickListener helpFabMenuAction() {
545 return new View.OnClickListener() {
546
547 /**
548 * Called when a view has been clicked.
549 *
550 * @param v The view that was clicked.
551 */
552 @Override
553 public void onClick(View v) {
554 ((KittyTutorialActivity) getParentFragment().getActivity()).showWebViewDialog(L5_T2_TUTORIAL);
555 }
556 };
557 }
558
559 @Override
560 public View.OnClickListener sourceFabMenuAction() {
561 return new View.OnClickListener() {
562
563 /**
564 * Called when a view has been clicked.
565 *
566 * @param v The view that was clicked.
567 */
568 @Override
569 public void onClick(View v) {
570 ((KittyTutorialActivity) getParentFragment().getActivity()).showWebViewDialog(L5_T2_SOURCE);
571 }
572 };
573 }
574
575 @Override
576 public View.OnClickListener schemaFabMenuAction() {
577 return new View.OnClickListener() {
578
579 /**
580 * Called when a view has been clicked.
581 *
582 * @param v The view that was clicked.
583 */
584 @Override
585 public void onClick(View v) {
586 ((KittyTutorialActivity) getParentFragment().getActivity()).showWebViewDialog(L5_T2_SCHEMA);
587 }
588 };
589 }
590}