Source code

KittyORM mig v.3 implementation sources
  1. Click to view MigrationDBv3.class:
     1@KITTY_DATABASE(
     2        isLoggingOn = true,
     3        isProductionOn = false,
     4        isKittyDexUtilLoggingEnabled = false,
     5        logTag = MigrationDBv3.LTAG,
     6        databaseName = "mig",
     7        databaseVersion = 3,
     8        domainPackageNames = {"net.akaish.kittyormdemo.sqlite.migrations.migv3"}
     9)
    10@KITTY_DATABASE_REGISTRY(
    11        domainModels = {
    12                net.akaish.kittyormdemo.sqlite.migrations.migv3.MigOneModel.class,
    13                net.akaish.kittyormdemo.sqlite.migrations.migv3.MigTwoModel.class,
    14                net.akaish.kittyormdemo.sqlite.migrations.migv3.MigThreeModel.class
    15        }
    16)
    17@KITTY_DATABASE_HELPER(
    18        onUpgradeBehavior = KITTY_DATABASE_HELPER.UpgradeBehavior.USE_SIMPLE_MIGRATIONS
    19)
    20public class MigrationDBv3 extends KittyDatabase {
    21
    22    public static final String LTAG = "MIGv3";
    23    /**
    24     * KittyORM main database class that represents bootstrap and holder for all related with database
    25     * components.
    26     *
    27     * @param ctx
    28     */
    29    public MigrationDBv3(Context ctx) {
    30        super(ctx);
    31    }
    32}

  2. Click to view MigOneModel.class:
     1@KITTY_TABLE(
     2        tableName = "mig_one"
     3)
     4public class MigOneModel extends KittyModel {
     5    @KITTY_COLUMN(
     6            columnOrder = 0,
     7            isIPK = true)
     8    public Long id;
     9
    10    @KITTY_COLUMN(
    11            columnOrder = 1
    12    )
    13    @NOT_NULL
    14    @DEFAULT(predefinedLiteralValue = LiteralValues.CURRENT_DATE)
    15    public String creationDate;
    16
    17    @KITTY_COLUMN(
    18            columnOrder = 2
    19    )
    20    @DEFAULT(signedInteger = 228)
    21    @ONE_COLUMN_INDEX(indexName = "m1_di_index")
    22    public Integer defaultInteger;
    23
    24    public String toString() {
    25        return new StringBuilder(64)
    26                .append("[ id = ")
    27                .append(id)
    28                .append(" ; creationDate = ")
    29                .append(creationDate)
    30                .append(" ; someInteger = ")
    31                .append(defaultInteger)
    32                .append(" ]").toString();
    33    }
    34}

  3. Click to view MigTwoModel.class:
     1@KITTY_TABLE(
     2        tableName = "mig_two"
     3)
     4public class MigTwoModel extends KittyModel {
     5
     6    @KITTY_COLUMN(
     7            columnOrder = 0,
     8            isIPK = true
     9    )
    10    public Long id;
    11
    12    @KITTY_COLUMN(
    13            columnOrder = 1
    14    )
    15    @FOREIGN_KEY(
    16            reference = @FOREIGN_KEY_REFERENCE(
    17                    foreignTableName = "mig_one",
    18                    foreignTableColumns = {"id"},
    19                    onUpdate = OnUpdateDeleteActions.CASCADE,
    20                    onDelete = OnUpdateDeleteActions.CASCADE
    21            )
    22    )
    23    public Long migOneReference;
    24
    25    @KITTY_COLUMN(columnOrder = 2)
    26    public Animals someAnimal;
    27
    28    @KITTY_COLUMN(
    29            columnOrder = 3,
    30            columnAffinity = TypeAffinities.TEXT
    31    )
    32    @KITTY_COLUMN_SERIALIZATION
    33    public AnimalSounds someAnimalSound;
    34
    35    String someAnimalSoundSerialize() {
    36        if(someAnimalSound == null) return null;
    37        return new GsonBuilder().create().toJson(someAnimalSound);
    38    }
    39
    40    AnimalSounds someAnimalSoundDeserialize(String cvData) {
    41        if(cvData == null) return null;
    42        if(cvData.length() == 0) return null;
    43        return new GsonBuilder().create().fromJson(cvData, AnimalSounds.class);
    44    }
    45
    46    @Override
    47    public String toString() {
    48        return new StringBuilder(64)
    49                .append("[ id = ")
    50                .append(id)
    51                .append(" ; migOneReference = ")
    52                .append(migOneReference)
    53                .append(" ; someAnimal = ")
    54                .append(someAnimal)
    55                .append(" ; someAnimalSound = ")
    56                .append(someAnimalSoundSerialize())
    57                .append(" ] ").toString();
    58    }
    59}

  4. Click to view MigThreeModel.class:
     1@KITTY_TABLE(tableName = "mig_three")
     2public class MigThreeModel extends KittyModel {
     3
     4    @KITTY_COLUMN(
     5            columnOrder = 0,
     6            isIPK = true
     7    )
     8    public Long id;
     9
    10    @KITTY_COLUMN(
    11            columnOrder = 1
    12    )
    13    @NOT_NULL
    14    @DEFAULT(
    15            literalValue = "\'Something random\'"
    16    )
    17    public String someValue;
    18
    19    public String toString() {
    20        return new StringBuilder(32)
    21                .append("[ id = ")
    22                .append(id)
    23                .append(" ; someValue = ")
    24                .append(someValue)
    25                .append(" ]").toString();
    26    }
    27}

Fragment and utility code used in this tutorial
  1. Click to view MigV3RandomModelFactory.class:
     1public class MigV3RandomModelFactory {
     2    final Context context;
     3    final Random rnd;
     4
     5    private final SparseArray<String> randomAnimalSays = new SparseArray<>();
     6    private final SparseArray<String> randomAnimalLocalizedName = new SparseArray<>();
     7
     8    public MigV3RandomModelFactory(Context ctx) {
     9        this.context = ctx;
    10        this.rnd = new Random();
    11
    12        // Lol, getContext().getString() method is fucking slow, calling for each new random model this method twice causes 55% of all execution time of generating new random model (!)
    13        // Right now getting those string causes only 14% of execution time
    14        randomAnimalSays.append(Animals.getLocalizedAnimalSaysResource(Animals.BEAR), context.getString(Animals.getLocalizedAnimalSaysResource(Animals.BEAR)));
    15        randomAnimalSays.append(Animals.getLocalizedAnimalSaysResource(Animals.CAT), context.getString(Animals.getLocalizedAnimalSaysResource(Animals.CAT)));
    16        randomAnimalSays.append(Animals.getLocalizedAnimalSaysResource(Animals.DOG), context.getString(Animals.getLocalizedAnimalSaysResource(Animals.DOG)));
    17        randomAnimalSays.append(Animals.getLocalizedAnimalSaysResource(Animals.GOAT), context.getString(Animals.getLocalizedAnimalSaysResource(Animals.GOAT)));
    18        randomAnimalSays.append(Animals.getLocalizedAnimalSaysResource(Animals.LION), context.getString(Animals.getLocalizedAnimalSaysResource(Animals.LION)));
    19        randomAnimalSays.append(Animals.getLocalizedAnimalSaysResource(Animals.SHEEP), context.getString(Animals.getLocalizedAnimalSaysResource(Animals.SHEEP)));
    20        randomAnimalSays.append(Animals.getLocalizedAnimalSaysResource(Animals.TIGER), context.getString(Animals.getLocalizedAnimalSaysResource(Animals.TIGER)));
    21        randomAnimalSays.append(Animals.getLocalizedAnimalSaysResource(Animals.WOLF), context.getString(Animals.getLocalizedAnimalSaysResource(Animals.WOLF)));
    22
    23
    24        randomAnimalLocalizedName.append(Animals.getLocalizedAnimalNameResource(Animals.BEAR), context.getString(Animals.getLocalizedAnimalNameResource(Animals.BEAR)));
    25        randomAnimalLocalizedName.append(Animals.getLocalizedAnimalNameResource(Animals.CAT), context.getString(Animals.getLocalizedAnimalNameResource(Animals.CAT)));
    26        randomAnimalLocalizedName.append(Animals.getLocalizedAnimalNameResource(Animals.DOG), context.getString(Animals.getLocalizedAnimalNameResource(Animals.DOG)));
    27        randomAnimalLocalizedName.append(Animals.getLocalizedAnimalNameResource(Animals.GOAT), context.getString(Animals.getLocalizedAnimalNameResource(Animals.GOAT)));
    28        randomAnimalLocalizedName.append(Animals.getLocalizedAnimalNameResource(Animals.LION), context.getString(Animals.getLocalizedAnimalNameResource(Animals.LION)));
    29        randomAnimalLocalizedName.append(Animals.getLocalizedAnimalNameResource(Animals.SHEEP), context.getString(Animals.getLocalizedAnimalNameResource(Animals.SHEEP)));
    30        randomAnimalLocalizedName.append(Animals.getLocalizedAnimalNameResource(Animals.TIGER), context.getString(Animals.getLocalizedAnimalNameResource(Animals.TIGER)));
    31        randomAnimalLocalizedName.append(Animals.getLocalizedAnimalNameResource(Animals.WOLF), context.getString(Animals.getLocalizedAnimalNameResource(Animals.WOLF)));
    32    }
    33
    34    public MigOneModel newM1RndModel() {
    35        return newM1RndModel(rnd.nextBoolean(), rnd.nextBoolean());
    36    }
    37
    38    public MigOneModel newM1RndModel(boolean setCDDefault, boolean setDefaultInteger) {
    39        MigOneModel model = new MigOneModel();
    40        if(setCDDefault)
    41            model.setFieldExclusion("creationDate");
    42        else
    43            model.creationDate = new Date(System.currentTimeMillis()).toString();
    44        if(setCDDefault)
    45            model.setFieldExclusion("defaultInteger");
    46        else
    47            model.defaultInteger = rnd.nextInt();
    48        return model;
    49    }
    50
    51    public MigTwoModel newM2RndModel(ArrayList<MigOneModel> models) {
    52        if(models == null)
    53            throw new IllegalArgumentException("M3RMF#newM2RndModel bad model collection provided!");
    54        if(models.size() == 0)
    55            throw new IllegalArgumentException("M3RMF#newM2RndModel bad model collection provided!");
    56        int mlSize = models.size();
    57        return newM2RndModel(models.get(rnd.nextInt(mlSize)).id);
    58    }
    59
    60    public MigTwoModel newM2RndModel(Long migOneReference) {
    61        if(migOneReference == null)
    62            throw new IllegalArgumentException("M3RMF#newM2RndModel bad reference id provided!");
    63        MigTwoModel model = new MigTwoModel();
    64        model.someAnimal = Animals.rndAnimal(rnd);
    65        model.migOneReference = migOneReference;
    66        AnimalSounds animalSounds = new AnimalSounds();
    67        animalSounds.animalName = randomAnimalLocalizedName.get(Animals.getLocalizedAnimalNameResource(model.someAnimal));
    68        animalSounds.animalSounds = randomAnimalSays.get(Animals.getLocalizedAnimalSaysResource(model.someAnimal));
    69        model.someAnimalSound = animalSounds;
    70        return model;
    71    }
    72
    73    static final String[] M3_SOME_VALUES = {"One", "Apple", "Wolf", "Plane", "Name", "Fear of being alone", "Despair", "Death", "Do not look for meaning where it is not"};
    74
    75    public MigThreeModel newM3RndModel() {
    76        return newM3RndModel(rnd.nextBoolean());
    77    }
    78
    79    public MigThreeModel newM3RndModel(boolean setDefaultValue) {
    80        MigThreeModel model = new MigThreeModel();
    81        if(setDefaultValue)
    82            model.setFieldExclusion("someValue");
    83        else
    84            model.someValue = M3_SOME_VALUES[rnd.nextInt(M3_SOME_VALUES.length)];
    85        return model;
    86    }
    87}

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