Compare commits
	
		
			13 Commits
		
	
	
		
			4741478623
			...
			develop-an
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 73f7df058c | |||
| 6ad7586ce9 | |||
| 3ad5cbc9c8 | |||
| 0ec6587ff2 | |||
| 81f9037ceb | |||
| 6d3893d40d | |||
| bdfa1e9e86 | |||
| 6a5b6492e3 | |||
| 688cfd3397 | |||
| ca41628377 | |||
| 00e729e677 | |||
| fd8ab9cca4 | |||
| 11dc71b424 | 
							
								
								
									
										2
									
								
								Android/.idea/compiler.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								Android/.idea/compiler.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||||
| <project version="4"> | <project version="4"> | ||||||
|   <component name="CompilerConfiguration"> |   <component name="CompilerConfiguration"> | ||||||
|     <bytecodeTargetLevel target="15" /> |     <bytecodeTargetLevel target="16" /> | ||||||
|   </component> |   </component> | ||||||
| </project> | </project> | ||||||
							
								
								
									
										2
									
								
								Android/.idea/gradle.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								Android/.idea/gradle.xml
									
									
									
										generated
									
									
									
								
							| @@ -7,7 +7,7 @@ | |||||||
|         <option name="testRunner" value="GRADLE" /> |         <option name="testRunner" value="GRADLE" /> | ||||||
|         <option name="distributionType" value="DEFAULT_WRAPPED" /> |         <option name="distributionType" value="DEFAULT_WRAPPED" /> | ||||||
|         <option name="externalProjectPath" value="$PROJECT_DIR$" /> |         <option name="externalProjectPath" value="$PROJECT_DIR$" /> | ||||||
|         <option name="gradleJvm" value="openjdk-15" /> |         <option name="gradleJvm" value="Android Studio java home" /> | ||||||
|         <option name="modules"> |         <option name="modules"> | ||||||
|           <set> |           <set> | ||||||
|             <option value="$PROJECT_DIR$" /> |             <option value="$PROJECT_DIR$" /> | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								Android/.idea/misc.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										16
									
								
								Android/.idea/misc.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,5 +1,19 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||||
| <project version="4"> | <project version="4"> | ||||||
|  |   <component name="DesignSurface"> | ||||||
|  |     <option name="filePathToZoomLevelMap"> | ||||||
|  |       <map> | ||||||
|  |         <entry key="app/src/main/res/layout/dropdown_list_item.xml" value="0.12777777777777777" /> | ||||||
|  |         <entry key="app/src/main/res/layout/fragment_dashboard_list_item.xml" value="0.2210144927536232" /> | ||||||
|  |         <entry key="app/src/main/res/layout/fragment_edit_strings_list.xml" value="0.2210144927536232" /> | ||||||
|  |         <entry key="app/src/main/res/layout/fragment_edit_strings_list_item.xml" value="0.2210144927536232" /> | ||||||
|  |         <entry key="app/src/main/res/layout/fragment_edit_traits_list.xml" value="0.2210144927536232" /> | ||||||
|  |         <entry key="app/src/main/res/layout/fragment_edit_traits_list_item.xml" value="0.2210144927536232" /> | ||||||
|  |         <entry key="app/src/main/res/layout/fragment_search.xml" value="0.16875" /> | ||||||
|  |         <entry key="app/src/main/res/layout/simple_list_item.xml" value="0.2210144927536232" /> | ||||||
|  |       </map> | ||||||
|  |     </option> | ||||||
|  |   </component> | ||||||
|   <component name="NullableNotNullManager"> |   <component name="NullableNotNullManager"> | ||||||
|     <option name="myDefaultNullable" value="androidx.annotation.Nullable" /> |     <option name="myDefaultNullable" value="androidx.annotation.Nullable" /> | ||||||
|     <option name="myDefaultNotNull" value="androidx.annotation.NonNull" /> |     <option name="myDefaultNotNull" value="androidx.annotation.NonNull" /> | ||||||
| @@ -44,7 +58,7 @@ | |||||||
|       </value> |       </value> | ||||||
|     </option> |     </option> | ||||||
|   </component> |   </component> | ||||||
|   <component name="ProjectRootManager" version="2" languageLevel="JDK_15" project-jdk-name="1.8" project-jdk-type="JavaSDK"> |   <component name="ProjectRootManager" version="2" languageLevel="JDK_X" project-jdk-name="1.8" project-jdk-type="JavaSDK"> | ||||||
|     <output url="file://$PROJECT_DIR$/build/classes" /> |     <output url="file://$PROJECT_DIR$/build/classes" /> | ||||||
|   </component> |   </component> | ||||||
|   <component name="ProjectType"> |   <component name="ProjectType"> | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| plugins { | plugins { | ||||||
|     id 'com.android.application' |     id 'com.android.application' | ||||||
|     id 'androidx.navigation.safeargs' |     id 'androidx.navigation.safeargs' | ||||||
|     id 'kotlin-android' |  | ||||||
| } | } | ||||||
|  |  | ||||||
| Properties properties = new Properties() | Properties properties = new Properties() | ||||||
| @@ -66,8 +65,6 @@ dependencies { | |||||||
|     // Included libs |     // Included libs | ||||||
|     implementation fileTree(dir: "libs", include: ["*.jar"]) |     implementation fileTree(dir: "libs", include: ["*.jar"]) | ||||||
|  |  | ||||||
|     implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.20" |  | ||||||
|  |  | ||||||
|     // Google |     // Google | ||||||
|     implementation 'androidx.appcompat:appcompat:1.3.1' |     implementation 'androidx.appcompat:appcompat:1.3.1' | ||||||
|     implementation 'com.google.android.material:material:1.4.0' |     implementation 'com.google.android.material:material:1.4.0' | ||||||
|   | |||||||
| @@ -1,8 +1,14 @@ | |||||||
| package com.majinnaibu.monstercards; | package com.majinnaibu.monstercards; | ||||||
|  |  | ||||||
|  | import android.content.Context; | ||||||
|  |  | ||||||
|  | import androidx.annotation.NonNull; | ||||||
| import androidx.room.Database; | import androidx.room.Database; | ||||||
|  | import androidx.room.Room; | ||||||
| import androidx.room.RoomDatabase; | import androidx.room.RoomDatabase; | ||||||
| import androidx.room.TypeConverters; | import androidx.room.TypeConverters; | ||||||
|  | import androidx.room.migration.Migration; | ||||||
|  | import androidx.sqlite.db.SupportSQLiteDatabase; | ||||||
|  |  | ||||||
| import com.majinnaibu.monstercards.data.MonsterDAO; | import com.majinnaibu.monstercards.data.MonsterDAO; | ||||||
| import com.majinnaibu.monstercards.data.converters.ArmorTypeConverter; | import com.majinnaibu.monstercards.data.converters.ArmorTypeConverter; | ||||||
| @@ -26,5 +32,47 @@ import com.majinnaibu.monstercards.models.MonsterFTS; | |||||||
|         UUIDConverter.class, |         UUIDConverter.class, | ||||||
| }) | }) | ||||||
| public abstract class AppDatabase extends RoomDatabase { | public abstract class AppDatabase extends RoomDatabase { | ||||||
|  |     private static final Migration MIGRATION_1_2 = new Migration(1, 2) { | ||||||
|  |         @Override | ||||||
|  |         public void migrate(@NonNull SupportSQLiteDatabase database) { | ||||||
|  |             // rename table monster to monsters | ||||||
|  |             database.execSQL("ALTER TABLE monster RENAME TO monsters"); | ||||||
|  |             // create the fts view | ||||||
|  |             database.execSQL("CREATE VIRTUAL TABLE IF NOT EXISTS `monsters_fts` USING FTS4(`name` TEXT, `size` TEXT, `type` TEXT, `subtype` TEXT, `alignment` TEXT, content=`monsters`)"); | ||||||
|  |             // build the initial full text search index | ||||||
|  |             database.execSQL("INSERT INTO monsters_fts(monsters_fts) VALUES('rebuild')"); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     private static final Migration MIGRATION_2_3 = new Migration(2, 3) { | ||||||
|  |         @Override | ||||||
|  |         public void migrate(@NonNull SupportSQLiteDatabase database) { | ||||||
|  |             // Add the senses column | ||||||
|  |             database.execSQL("ALTER TABLE monsters ADD COLUMN 'senses' TEXT DEFAULT '[]'"); | ||||||
|  |             database.execSQL("CREATE TABLE new_monsters (`id` TEXT NOT NULL, `name` TEXT NOT NULL DEFAULT '', `size` TEXT NOT NULL DEFAULT '', `type` TEXT NOT NULL DEFAULT '', `subtype` TEXT NOT NULL DEFAULT '', `alignment` TEXT NOT NULL DEFAULT '', `strength_score` INTEGER NOT NULL DEFAULT 10, `strength_saving_throw_advantage` TEXT DEFAULT 'none', `strength_saving_throw_proficiency` TEXT DEFAULT 'none', `dexterity_score` INTEGER NOT NULL DEFAULT 10, `dexterity_saving_throw_advantage` TEXT DEFAULT 'none', `dexterity_saving_throw_proficiency` TEXT DEFAULT 'none', `constitution_score` INTEGER NOT NULL DEFAULT 10, `constitution_saving_throw_advantage` TEXT DEFAULT 'none', `constitution_saving_throw_proficiency` TEXT DEFAULT 'none', `intelligence_score` INTEGER NOT NULL DEFAULT 10, `intelligence_saving_throw_advantage` TEXT DEFAULT 'none', `intelligence_saving_throw_proficiency` TEXT DEFAULT 'none', `wisdom_score` INTEGER NOT NULL DEFAULT 10, `wisdom_saving_throw_advantage` TEXT DEFAULT 'none', `wisdom_saving_throw_proficiency` TEXT DEFAULT 'none', `charisma_score` INTEGER NOT NULL DEFAULT 10, `charisma_saving_throw_advantage` TEXT DEFAULT 'none', `charisma_saving_throw_proficiency` TEXT DEFAULT 'none', `armor_type` TEXT DEFAULT 'none', `shield_bonus` INTEGER NOT NULL DEFAULT 0, `natural_armor_bonus` INTEGER NOT NULL DEFAULT 0, `other_armor_description` TEXT DEFAULT '', `hit_dice` INTEGER NOT NULL DEFAULT 1, `has_custom_hit_points` INTEGER NOT NULL, `custom_hit_points_description` TEXT DEFAULT '', `walk_speed` INTEGER NOT NULL DEFAULT 0, `burrow_speed` INTEGER NOT NULL DEFAULT 0, `climb_speed` INTEGER NOT NULL DEFAULT 0, `fly_speed` INTEGER NOT NULL DEFAULT 0, `can_hover` INTEGER NOT NULL DEFAULT false, `swim_speed` INTEGER NOT NULL DEFAULT 0, `has_custom_speed` INTEGER NOT NULL DEFAULT false, `custom_speed_description` TEXT, `challenge_rating` TEXT DEFAULT '1', `custom_challenge_rating_description` TEXT DEFAULT '', `custom_proficiency_bonus` INTEGER NOT NULL DEFAULT 0, `telepathy_range` INTEGER NOT NULL DEFAULT 0, `understands_but_description` TEXT DEFAULT '', `senses` TEXT DEFAULT '[]', `skills` TEXT DEFAULT '[]', `damage_immunities` TEXT DEFAULT '[]', `damage_resistances` TEXT DEFAULT '[]', `damage_vulnerabilities` TEXT DEFAULT '[]', `condition_immunities` TEXT DEFAULT '[]', `languages` TEXT DEFAULT '[]', `abilities` TEXT DEFAULT '[]', `actions` TEXT DEFAULT '[]', `reactions` TEXT DEFAULT '[]', `lair_actions` TEXT DEFAULT '[]', `legendary_actions` TEXT DEFAULT '[]', `regional_actions` TEXT DEFAULT '[]', PRIMARY KEY(`id`))"); | ||||||
|  |             database.execSQL("INSERT INTO new_monsters(id, name, size, type, subtype, alignment, strength_score, strength_saving_throw_advantage, strength_saving_throw_proficiency, dexterity_score, dexterity_saving_throw_advantage, dexterity_saving_throw_proficiency, constitution_score, constitution_saving_throw_advantage, constitution_saving_throw_proficiency, intelligence_score, intelligence_saving_throw_advantage, intelligence_saving_throw_proficiency, wisdom_score, wisdom_saving_throw_advantage, wisdom_saving_throw_proficiency, charisma_score, charisma_saving_throw_advantage, charisma_saving_throw_proficiency, armor_type, shield_bonus, natural_armor_bonus, other_armor_description, hit_dice, has_custom_hit_points, custom_hit_points_description, walk_speed, burrow_speed, climb_speed, fly_speed, can_hover, swim_speed, has_custom_speed, custom_speed_description, challenge_rating, custom_challenge_rating_description, custom_proficiency_bonus, telepathy_range, understands_but_description, senses, skills, damage_immunities, damage_resistances, damage_vulnerabilities, condition_immunities, languages, abilities, actions, reactions, lair_actions, legendary_actions, regional_actions) SELECT id, name, size, type, subtype, alignment, strength_score, strength_saving_throw_advantage, strength_saving_throw_proficiency, dexterity_score, dexterity_saving_throw_advantage, dexterity_saving_throw_proficiency, constitution_score, constitution_saving_throw_advantage, constitution_saving_throw_proficiency, intelligence_score, intelligence_saving_throw_advantage, intelligence_saving_throw_proficiency, wisdom_score, wisdom_saving_throw_advantage, wisdom_saving_throw_proficiency, charisma_score, charisma_saving_throw_advantage, charisma_saving_throw_proficiency, armor_type, shield_bonus, natural_armor_bonus, other_armor_description, hit_dice, has_custom_hit_points, custom_hit_points_description, walk_speed, burrow_speed, climb_speed, fly_speed, can_hover, swim_speed, has_custom_speed, custom_speed_description, challenge_rating, custom_challenge_rating_description, custom_proficiency_bonus, telepathy_range, understands_but_description, senses, skills, damage_immunities, damage_resistances, damage_vulnerabilities, condition_immunities, languages, abilities, actions, reactions, lair_actions, legendary_actions, regional_actions FROM monsters"); | ||||||
|  |             database.execSQL("DROP TABLE monsters"); | ||||||
|  |             database.execSQL("ALTER TABLE new_monsters RENAME TO monsters"); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     private static AppDatabase mDB = null; | ||||||
|  |  | ||||||
|  |     public static AppDatabase getInstance(Context context) { | ||||||
|  |         if (mDB == null) { | ||||||
|  |             synchronized (AppDatabase.class) { | ||||||
|  |                 if (mDB == null) { | ||||||
|  |                     //                .fallbackToDestructiveMigration() | ||||||
|  |                     mDB = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "monsters") | ||||||
|  |                             .addMigrations(MIGRATION_1_2) | ||||||
|  |                             .addMigrations(MIGRATION_2_3) | ||||||
|  |                             .fallbackToDestructiveMigrationOnDowngrade() | ||||||
|  | //                .fallbackToDestructiveMigration() | ||||||
|  |                             .build(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return mDB; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public abstract MonsterDAO monsterDAO(); |     public abstract MonsterDAO monsterDAO(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,39 +3,12 @@ package com.majinnaibu.monstercards; | |||||||
| import android.app.Application; | import android.app.Application; | ||||||
| import android.content.res.Configuration; | import android.content.res.Configuration; | ||||||
|  |  | ||||||
| import androidx.annotation.NonNull; |  | ||||||
| import androidx.room.Room; |  | ||||||
| import androidx.room.migration.Migration; |  | ||||||
| import androidx.sqlite.db.SupportSQLiteDatabase; |  | ||||||
|  |  | ||||||
| import com.majinnaibu.monstercards.data.MonsterRepository; | import com.majinnaibu.monstercards.data.MonsterRepository; | ||||||
| import com.majinnaibu.monstercards.init.FlipperInitializer; | import com.majinnaibu.monstercards.init.FlipperInitializer; | ||||||
|  |  | ||||||
| public class MonsterCardsApplication extends Application { | public class MonsterCardsApplication extends Application { | ||||||
|  |  | ||||||
|     private static final Migration MIGRATION_1_2 = new Migration(1, 2) { |  | ||||||
|         @Override |  | ||||||
|         public void migrate(@NonNull SupportSQLiteDatabase database) { |  | ||||||
|             // rename table monster to monsters |  | ||||||
|             database.execSQL("ALTER TABLE monster RENAME TO monsters"); |  | ||||||
|             // create the fts view |  | ||||||
|             database.execSQL("CREATE VIRTUAL TABLE IF NOT EXISTS `monsters_fts` USING FTS4(`name` TEXT, `size` TEXT, `type` TEXT, `subtype` TEXT, `alignment` TEXT, content=`monsters`)"); |  | ||||||
|             // build the initial full text search index |  | ||||||
|             database.execSQL("INSERT INTO monsters_fts(monsters_fts) VALUES('rebuild')"); |  | ||||||
|  |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|     private static final Migration MIGRATION_2_3 = new Migration(2, 3) { |  | ||||||
|         @Override |  | ||||||
|         public void migrate(@NonNull SupportSQLiteDatabase database) { |  | ||||||
|             // Add the senses column |  | ||||||
|             database.execSQL("ALTER TABLE monsters ADD COLUMN 'senses' TEXT DEFAULT '[]'"); |  | ||||||
|             database.execSQL("CREATE TABLE new_monsters (`id` TEXT NOT NULL, `name` TEXT NOT NULL DEFAULT '', `size` TEXT NOT NULL DEFAULT '', `type` TEXT NOT NULL DEFAULT '', `subtype` TEXT NOT NULL DEFAULT '', `alignment` TEXT NOT NULL DEFAULT '', `strength_score` INTEGER NOT NULL DEFAULT 10, `strength_saving_throw_advantage` TEXT DEFAULT 'none', `strength_saving_throw_proficiency` TEXT DEFAULT 'none', `dexterity_score` INTEGER NOT NULL DEFAULT 10, `dexterity_saving_throw_advantage` TEXT DEFAULT 'none', `dexterity_saving_throw_proficiency` TEXT DEFAULT 'none', `constitution_score` INTEGER NOT NULL DEFAULT 10, `constitution_saving_throw_advantage` TEXT DEFAULT 'none', `constitution_saving_throw_proficiency` TEXT DEFAULT 'none', `intelligence_score` INTEGER NOT NULL DEFAULT 10, `intelligence_saving_throw_advantage` TEXT DEFAULT 'none', `intelligence_saving_throw_proficiency` TEXT DEFAULT 'none', `wisdom_score` INTEGER NOT NULL DEFAULT 10, `wisdom_saving_throw_advantage` TEXT DEFAULT 'none', `wisdom_saving_throw_proficiency` TEXT DEFAULT 'none', `charisma_score` INTEGER NOT NULL DEFAULT 10, `charisma_saving_throw_advantage` TEXT DEFAULT 'none', `charisma_saving_throw_proficiency` TEXT DEFAULT 'none', `armor_type` TEXT DEFAULT 'none', `shield_bonus` INTEGER NOT NULL DEFAULT 0, `natural_armor_bonus` INTEGER NOT NULL DEFAULT 0, `other_armor_description` TEXT DEFAULT '', `hit_dice` INTEGER NOT NULL DEFAULT 1, `has_custom_hit_points` INTEGER NOT NULL, `custom_hit_points_description` TEXT DEFAULT '', `walk_speed` INTEGER NOT NULL DEFAULT 0, `burrow_speed` INTEGER NOT NULL DEFAULT 0, `climb_speed` INTEGER NOT NULL DEFAULT 0, `fly_speed` INTEGER NOT NULL DEFAULT 0, `can_hover` INTEGER NOT NULL DEFAULT false, `swim_speed` INTEGER NOT NULL DEFAULT 0, `has_custom_speed` INTEGER NOT NULL DEFAULT false, `custom_speed_description` TEXT, `challenge_rating` TEXT DEFAULT '1', `custom_challenge_rating_description` TEXT DEFAULT '', `custom_proficiency_bonus` INTEGER NOT NULL DEFAULT 0, `telepathy_range` INTEGER NOT NULL DEFAULT 0, `understands_but_description` TEXT DEFAULT '', `senses` TEXT DEFAULT '[]', `skills` TEXT DEFAULT '[]', `damage_immunities` TEXT DEFAULT '[]', `damage_resistances` TEXT DEFAULT '[]', `damage_vulnerabilities` TEXT DEFAULT '[]', `condition_immunities` TEXT DEFAULT '[]', `languages` TEXT DEFAULT '[]', `abilities` TEXT DEFAULT '[]', `actions` TEXT DEFAULT '[]', `reactions` TEXT DEFAULT '[]', `lair_actions` TEXT DEFAULT '[]', `legendary_actions` TEXT DEFAULT '[]', `regional_actions` TEXT DEFAULT '[]', PRIMARY KEY(`id`))"); |  | ||||||
|             database.execSQL("INSERT INTO new_monsters(id, name, size, type, subtype, alignment, strength_score, strength_saving_throw_advantage, strength_saving_throw_proficiency, dexterity_score, dexterity_saving_throw_advantage, dexterity_saving_throw_proficiency, constitution_score, constitution_saving_throw_advantage, constitution_saving_throw_proficiency, intelligence_score, intelligence_saving_throw_advantage, intelligence_saving_throw_proficiency, wisdom_score, wisdom_saving_throw_advantage, wisdom_saving_throw_proficiency, charisma_score, charisma_saving_throw_advantage, charisma_saving_throw_proficiency, armor_type, shield_bonus, natural_armor_bonus, other_armor_description, hit_dice, has_custom_hit_points, custom_hit_points_description, walk_speed, burrow_speed, climb_speed, fly_speed, can_hover, swim_speed, has_custom_speed, custom_speed_description, challenge_rating, custom_challenge_rating_description, custom_proficiency_bonus, telepathy_range, understands_but_description, senses, skills, damage_immunities, damage_resistances, damage_vulnerabilities, condition_immunities, languages, abilities, actions, reactions, lair_actions, legendary_actions, regional_actions) SELECT id, name, size, type, subtype, alignment, strength_score, strength_saving_throw_advantage, strength_saving_throw_proficiency, dexterity_score, dexterity_saving_throw_advantage, dexterity_saving_throw_proficiency, constitution_score, constitution_saving_throw_advantage, constitution_saving_throw_proficiency, intelligence_score, intelligence_saving_throw_advantage, intelligence_saving_throw_proficiency, wisdom_score, wisdom_saving_throw_advantage, wisdom_saving_throw_proficiency, charisma_score, charisma_saving_throw_advantage, charisma_saving_throw_proficiency, armor_type, shield_bonus, natural_armor_bonus, other_armor_description, hit_dice, has_custom_hit_points, custom_hit_points_description, walk_speed, burrow_speed, climb_speed, fly_speed, can_hover, swim_speed, has_custom_speed, custom_speed_description, challenge_rating, custom_challenge_rating_description, custom_proficiency_bonus, telepathy_range, understands_but_description, senses, skills, damage_immunities, damage_resistances, damage_vulnerabilities, condition_immunities, languages, abilities, actions, reactions, lair_actions, legendary_actions, regional_actions FROM monsters"); |  | ||||||
|             database.execSQL("DROP TABLE monsters"); |  | ||||||
|             database.execSQL("ALTER TABLE new_monsters RENAME TO monsters"); |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|     private MonsterRepository m_monsterLibraryRepository; |     private MonsterRepository m_monsterLibraryRepository; | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -54,15 +27,8 @@ public class MonsterCardsApplication extends Application { | |||||||
|         // Required initialization logic here! |         // Required initialization logic here! | ||||||
|  |  | ||||||
|         FlipperInitializer.init(this); |         FlipperInitializer.init(this); | ||||||
|  |         AppDatabase mDB = AppDatabase.getInstance(this); | ||||||
|         //                .fallbackToDestructiveMigration() |         m_monsterLibraryRepository = new MonsterRepository(mDB); | ||||||
|         AppDatabase m_db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "monsters") |  | ||||||
|                 .addMigrations(MIGRATION_1_2) |  | ||||||
|                 .addMigrations(MIGRATION_2_3) |  | ||||||
|                 .fallbackToDestructiveMigrationOnDowngrade() |  | ||||||
| //                .fallbackToDestructiveMigration() |  | ||||||
|                 .build(); |  | ||||||
|         m_monsterLibraryRepository = new MonsterRepository(m_db); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Called by the system when the device configuration changes while your component is running. |     // Called by the system when the device configuration changes while your component is running. | ||||||
|   | |||||||
| @@ -0,0 +1,19 @@ | |||||||
|  | package com.majinnaibu.monstercards.data.converters; | ||||||
|  |  | ||||||
|  | import androidx.annotation.NonNull; | ||||||
|  | import androidx.room.TypeConverter; | ||||||
|  |  | ||||||
|  | import com.majinnaibu.monstercards.data.enums.ArmorType; | ||||||
|  |  | ||||||
|  | public class ArmorTypeConverter { | ||||||
|  |  | ||||||
|  |     @TypeConverter | ||||||
|  |     public static String fromArmorType(@NonNull ArmorType armorType) { | ||||||
|  |         return armorType.stringValue; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @TypeConverter | ||||||
|  |     public static ArmorType armorTypeFromStringValue(String stringValue) { | ||||||
|  |         return ArmorType.valueOfString(stringValue); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,19 +0,0 @@ | |||||||
| package com.majinnaibu.monstercards.data.converters |  | ||||||
|  |  | ||||||
| import androidx.room.TypeConverter |  | ||||||
| import com.majinnaibu.monstercards.data.enums.ArmorType |  | ||||||
| import com.majinnaibu.monstercards.data.enums.ArmorType.Companion.valueOfString |  | ||||||
|  |  | ||||||
| object ArmorTypeConverter { |  | ||||||
|     @JvmStatic |  | ||||||
|     @TypeConverter |  | ||||||
|     fun fromArmorType(armorType: ArmorType): String { |  | ||||||
|         return armorType.stringValue |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @JvmStatic |  | ||||||
|     @TypeConverter |  | ||||||
|     fun armorTypeFromStringValue(stringValue: String?): ArmorType { |  | ||||||
|         return valueOfString(stringValue!!) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,19 @@ | |||||||
|  | package com.majinnaibu.monstercards.data.converters; | ||||||
|  |  | ||||||
|  | import androidx.annotation.NonNull; | ||||||
|  | import androidx.room.TypeConverter; | ||||||
|  |  | ||||||
|  | import com.majinnaibu.monstercards.data.enums.ChallengeRating; | ||||||
|  |  | ||||||
|  | public class ChallengeRatingConverter { | ||||||
|  |  | ||||||
|  |     @TypeConverter | ||||||
|  |     public static String fromChallengeRating(@NonNull ChallengeRating challengeRating) { | ||||||
|  |         return challengeRating.stringValue; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @TypeConverter | ||||||
|  |     public static ChallengeRating challengeRatingFromStringValue(String stringValue) { | ||||||
|  |         return ChallengeRating.valueOfString(stringValue); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,19 +0,0 @@ | |||||||
| package com.majinnaibu.monstercards.data.converters |  | ||||||
|  |  | ||||||
| import androidx.room.TypeConverter |  | ||||||
| import com.majinnaibu.monstercards.data.enums.ChallengeRating |  | ||||||
| import com.majinnaibu.monstercards.data.enums.ChallengeRating.Companion.valueOfString |  | ||||||
|  |  | ||||||
| object ChallengeRatingConverter { |  | ||||||
|     @JvmStatic |  | ||||||
|     @TypeConverter |  | ||||||
|     fun fromChallengeRating(challengeRating: ChallengeRating): String { |  | ||||||
|         return challengeRating.stringValue |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @JvmStatic |  | ||||||
|     @TypeConverter |  | ||||||
|     fun challengeRatingFromStringValue(stringValue: String?): ChallengeRating { |  | ||||||
|         return valueOfString(stringValue!!) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,27 @@ | |||||||
|  | package com.majinnaibu.monstercards.data.converters; | ||||||
|  |  | ||||||
|  | import androidx.room.TypeConverter; | ||||||
|  |  | ||||||
|  | import com.google.gson.Gson; | ||||||
|  | import com.google.gson.reflect.TypeToken; | ||||||
|  | import com.majinnaibu.monstercards.models.Trait; | ||||||
|  |  | ||||||
|  | import java.lang.reflect.Type; | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | public class ListOfTraitsConverter { | ||||||
|  |     @TypeConverter | ||||||
|  |     public static String fromListOfTraits(List<Trait> traits) { | ||||||
|  |         Gson gson = new Gson(); | ||||||
|  |         return gson.toJson(traits); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @TypeConverter | ||||||
|  |     public static List<Trait> listOfTraitsFromString(String string) { | ||||||
|  |         Gson gson = new Gson(); | ||||||
|  |         Type setType = new TypeToken<ArrayList<Trait>>() { | ||||||
|  |         }.getType(); | ||||||
|  |         return gson.fromJson(string, setType); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,24 +0,0 @@ | |||||||
| package com.majinnaibu.monstercards.data.converters |  | ||||||
|  |  | ||||||
| import androidx.room.TypeConverter |  | ||||||
| import com.google.gson.Gson |  | ||||||
| import com.google.gson.reflect.TypeToken |  | ||||||
| import com.majinnaibu.monstercards.models.Trait |  | ||||||
| import java.util.* |  | ||||||
|  |  | ||||||
| object ListOfTraitsConverter { |  | ||||||
|     @JvmStatic |  | ||||||
|     @TypeConverter |  | ||||||
|     fun fromListOfTraits(traits: List<Trait?>?): String { |  | ||||||
|         val gson = Gson() |  | ||||||
|         return gson.toJson(traits) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @JvmStatic |  | ||||||
|     @TypeConverter |  | ||||||
|     fun listOfTraitsFromString(string: String?): List<Trait> { |  | ||||||
|         val gson = Gson() |  | ||||||
|         val setType = object : TypeToken<ArrayList<Trait?>?>() {}.type |  | ||||||
|         return gson.fromJson(string, setType) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,28 @@ | |||||||
|  | package com.majinnaibu.monstercards.data.converters; | ||||||
|  |  | ||||||
|  | import androidx.room.TypeConverter; | ||||||
|  |  | ||||||
|  | import com.google.gson.Gson; | ||||||
|  | import com.google.gson.reflect.TypeToken; | ||||||
|  | import com.majinnaibu.monstercards.models.Language; | ||||||
|  |  | ||||||
|  | import java.lang.reflect.Type; | ||||||
|  | import java.util.HashSet; | ||||||
|  | import java.util.Set; | ||||||
|  |  | ||||||
|  | public class SetOfLanguageConverter { | ||||||
|  |  | ||||||
|  |     @TypeConverter | ||||||
|  |     public static String fromSetOfLanguage(Set<Language> languages) { | ||||||
|  |         Gson gson = new Gson(); | ||||||
|  |         return gson.toJson(languages); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @TypeConverter | ||||||
|  |     public static Set<Language> setOfLanguageFromString(String string) { | ||||||
|  |         Gson gson = new Gson(); | ||||||
|  |         Type setType = new TypeToken<HashSet<Language>>() { | ||||||
|  |         }.getType(); | ||||||
|  |         return gson.fromJson(string, setType); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,24 +0,0 @@ | |||||||
| package com.majinnaibu.monstercards.data.converters |  | ||||||
|  |  | ||||||
| import androidx.room.TypeConverter |  | ||||||
| import com.google.gson.Gson |  | ||||||
| import com.google.gson.reflect.TypeToken |  | ||||||
| import com.majinnaibu.monstercards.models.Language |  | ||||||
| import java.util.* |  | ||||||
|  |  | ||||||
| object SetOfLanguageConverter { |  | ||||||
|     @JvmStatic |  | ||||||
|     @TypeConverter |  | ||||||
|     fun fromSetOfLanguage(languages: Set<Language?>?): String { |  | ||||||
|         val gson = Gson() |  | ||||||
|         return gson.toJson(languages) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @JvmStatic |  | ||||||
|     @TypeConverter |  | ||||||
|     fun setOfLanguageFromString(string: String?): Set<Language> { |  | ||||||
|         val gson = Gson() |  | ||||||
|         val setType = object : TypeToken<HashSet<Language?>?>() {}.type |  | ||||||
|         return gson.fromJson(string, setType) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,28 @@ | |||||||
|  | package com.majinnaibu.monstercards.data.converters; | ||||||
|  |  | ||||||
|  | import androidx.room.TypeConverter; | ||||||
|  |  | ||||||
|  | import com.google.gson.Gson; | ||||||
|  | import com.google.gson.reflect.TypeToken; | ||||||
|  | import com.majinnaibu.monstercards.models.Skill; | ||||||
|  |  | ||||||
|  | import java.lang.reflect.Type; | ||||||
|  | import java.util.HashSet; | ||||||
|  | import java.util.Set; | ||||||
|  |  | ||||||
|  | public class SetOfSkillConverter { | ||||||
|  |  | ||||||
|  |     @TypeConverter | ||||||
|  |     public static String fromSetOfSkill(Set<Skill> skills) { | ||||||
|  |         Gson gson = new Gson(); | ||||||
|  |         return gson.toJson(skills); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @TypeConverter | ||||||
|  |     public static Set<Skill> setOfSkillFromString(String string) { | ||||||
|  |         Gson gson = new Gson(); | ||||||
|  |         Type setType = new TypeToken<HashSet<Skill>>() { | ||||||
|  |         }.getType(); | ||||||
|  |         return gson.fromJson(string, setType); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,24 +0,0 @@ | |||||||
| package com.majinnaibu.monstercards.data.converters |  | ||||||
|  |  | ||||||
| import androidx.room.TypeConverter |  | ||||||
| import com.google.gson.Gson |  | ||||||
| import com.google.gson.reflect.TypeToken |  | ||||||
| import com.majinnaibu.monstercards.models.Skill |  | ||||||
| import java.util.* |  | ||||||
|  |  | ||||||
| object SetOfSkillConverter { |  | ||||||
|     @JvmStatic |  | ||||||
|     @TypeConverter |  | ||||||
|     fun fromSetOfSkill(skills: Set<Skill?>?): String { |  | ||||||
|         val gson = Gson() |  | ||||||
|         return gson.toJson(skills) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @JvmStatic |  | ||||||
|     @TypeConverter |  | ||||||
|     fun setOfSkillFromString(string: String?): Set<Skill> { |  | ||||||
|         val gson = Gson() |  | ||||||
|         val setType = object : TypeToken<HashSet<Skill?>?>() {}.type |  | ||||||
|         return gson.fromJson(string, setType) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,27 @@ | |||||||
|  | package com.majinnaibu.monstercards.data.converters; | ||||||
|  |  | ||||||
|  | import androidx.room.TypeConverter; | ||||||
|  |  | ||||||
|  | import com.google.gson.Gson; | ||||||
|  | import com.google.gson.reflect.TypeToken; | ||||||
|  |  | ||||||
|  | import java.lang.reflect.Type; | ||||||
|  | import java.util.HashSet; | ||||||
|  | import java.util.Set; | ||||||
|  |  | ||||||
|  | public class SetOfStringConverter { | ||||||
|  |  | ||||||
|  |     @TypeConverter | ||||||
|  |     public static String fromSetOfString(Set<String> strings) { | ||||||
|  |         Gson gson = new Gson(); | ||||||
|  |         return gson.toJson(strings); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @TypeConverter | ||||||
|  |     public static Set<String> setOfStringFromString(String string) { | ||||||
|  |         Gson gson = new Gson(); | ||||||
|  |         Type setType = new TypeToken<HashSet<String>>() { | ||||||
|  |         }.getType(); | ||||||
|  |         return gson.fromJson(string, setType); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,23 +0,0 @@ | |||||||
| package com.majinnaibu.monstercards.data.converters |  | ||||||
|  |  | ||||||
| import androidx.room.TypeConverter |  | ||||||
| import com.google.gson.Gson |  | ||||||
| import com.google.gson.reflect.TypeToken |  | ||||||
| import java.util.* |  | ||||||
|  |  | ||||||
| object SetOfStringConverter { |  | ||||||
|     @JvmStatic |  | ||||||
|     @TypeConverter |  | ||||||
|     fun fromSetOfString(strings: Set<String?>?): String { |  | ||||||
|         val gson = Gson() |  | ||||||
|         return gson.toJson(strings) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @JvmStatic |  | ||||||
|     @TypeConverter |  | ||||||
|     fun setOfStringFromString(string: String?): Set<String> { |  | ||||||
|         val gson = Gson() |  | ||||||
|         val setType = object : TypeToken<HashSet<String?>?>() {}.type |  | ||||||
|         return gson.fromJson(string, setType) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,20 @@ | |||||||
|  | package com.majinnaibu.monstercards.data.converters; | ||||||
|  |  | ||||||
|  | import androidx.annotation.NonNull; | ||||||
|  | import androidx.room.TypeConverter; | ||||||
|  |  | ||||||
|  | import java.util.UUID; | ||||||
|  |  | ||||||
|  | public class UUIDConverter { | ||||||
|  |  | ||||||
|  |     @NonNull | ||||||
|  |     @TypeConverter | ||||||
|  |     public static String fromUUID(@NonNull UUID uuid) { | ||||||
|  |         return uuid.toString(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @TypeConverter | ||||||
|  |     public static UUID uuidFromString(String string) { | ||||||
|  |         return UUID.fromString(string); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,18 +0,0 @@ | |||||||
| package com.majinnaibu.monstercards.data.converters |  | ||||||
|  |  | ||||||
| import androidx.room.TypeConverter |  | ||||||
| import java.util.* |  | ||||||
|  |  | ||||||
| object UUIDConverter { |  | ||||||
|     @JvmStatic |  | ||||||
|     @TypeConverter |  | ||||||
|     fun fromUUID(uuid: UUID): String { |  | ||||||
|         return uuid.toString() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @JvmStatic |  | ||||||
|     @TypeConverter |  | ||||||
|     fun uuidFromString(string: String?): UUID { |  | ||||||
|         return UUID.fromString(string) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,31 @@ | |||||||
|  | package com.majinnaibu.monstercards.data.enums; | ||||||
|  |  | ||||||
|  | @SuppressWarnings("unused") | ||||||
|  | public enum AbilityScore { | ||||||
|  |     STRENGTH("strength", "Strength", "STR"), | ||||||
|  |     DEXTERITY("dexterity", "Dexterity", "DEX"), | ||||||
|  |     CONSTITUTION("constitution", "Constitution", "CON"), | ||||||
|  |     INTELLIGENCE("intelligence", "Intelligence", "INT"), | ||||||
|  |     WISDOM("wisdom", "Wisdom", "WIS"), | ||||||
|  |     CHARISMA("charisma", "Charisma", "CHA"), | ||||||
|  |     ; | ||||||
|  |  | ||||||
|  |     public final String displayName; | ||||||
|  |     public final String shortDisplayName; | ||||||
|  |     public final String stringValue; | ||||||
|  |  | ||||||
|  |     AbilityScore(String stringValue, String displayName, String shortDisplayName) { | ||||||
|  |         this.displayName = displayName; | ||||||
|  |         this.stringValue = stringValue; | ||||||
|  |         this.shortDisplayName = shortDisplayName; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static AbilityScore valueOfString(String string) { | ||||||
|  |         for (AbilityScore abilityScore : values()) { | ||||||
|  |             if (abilityScore.stringValue.equals(string)) { | ||||||
|  |                 return abilityScore; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return AbilityScore.STRENGTH; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,26 +0,0 @@ | |||||||
| package com.majinnaibu.monstercards.data.enums |  | ||||||
|  |  | ||||||
| enum class AbilityScore( |  | ||||||
|     @JvmField val stringValue: String, |  | ||||||
|     @JvmField val displayName: String, |  | ||||||
|     @JvmField val shortDisplayName: String |  | ||||||
| ) { |  | ||||||
|     STRENGTH("strength", "Strength", "STR"), |  | ||||||
|     DEXTERITY("dexterity", "Dexterity", "DEX"), |  | ||||||
|     CONSTITUTION("constitution", "Constitution", "CON"), |  | ||||||
|     INTELLIGENCE("intelligence", "Intelligence", "INT"), |  | ||||||
|     WISDOM("wisdom", "Wisdom", "WIS"), |  | ||||||
|     CHARISMA("charisma", "Charisma", "CHA"); |  | ||||||
|  |  | ||||||
|     companion object { |  | ||||||
|         @JvmStatic |  | ||||||
|         fun valueOfString(string: String): AbilityScore { |  | ||||||
|             for (abilityScore in values()) { |  | ||||||
|                 if (abilityScore.stringValue == string) { |  | ||||||
|                     return abilityScore |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             return STRENGTH |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,27 @@ | |||||||
|  | package com.majinnaibu.monstercards.data.enums; | ||||||
|  |  | ||||||
|  | public enum AdvantageType { | ||||||
|  |     NONE("none", "None", ""), | ||||||
|  |     ADVANTAGE("advantage", "Advantage", "A"), | ||||||
|  |     DISADVANTAGE("disadvantage", "Disadvantage", "D"), | ||||||
|  |     ; | ||||||
|  |  | ||||||
|  |     public final String displayName; | ||||||
|  |     public final String stringValue; | ||||||
|  |     public final String label; | ||||||
|  |  | ||||||
|  |     AdvantageType(String stringValue, String displayName, String label) { | ||||||
|  |         this.displayName = displayName; | ||||||
|  |         this.stringValue = stringValue; | ||||||
|  |         this.label = label; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static AdvantageType valueOfString(String string) { | ||||||
|  |         for (AdvantageType advantageType : values()) { | ||||||
|  |             if (advantageType.stringValue.equals(string)) { | ||||||
|  |                 return advantageType; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return AdvantageType.NONE; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,23 +0,0 @@ | |||||||
| package com.majinnaibu.monstercards.data.enums |  | ||||||
|  |  | ||||||
| enum class AdvantageType( |  | ||||||
|     val stringValue: String, |  | ||||||
|     val displayName: String, |  | ||||||
|     @JvmField val label: String |  | ||||||
| ) { |  | ||||||
|     NONE("none", "None", ""), |  | ||||||
|     ADVANTAGE("advantage", "Advantage", "A"), |  | ||||||
|     DISADVANTAGE("disadvantage", "Disadvantage", "D"); |  | ||||||
|  |  | ||||||
|     companion object { |  | ||||||
|         @JvmStatic |  | ||||||
|         fun valueOfString(string: String): AdvantageType { |  | ||||||
|             for (advantageType in values()) { |  | ||||||
|                 if (advantageType.stringValue == string) { |  | ||||||
|                     return advantageType |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             return NONE |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,10 +1,7 @@ | |||||||
| package com.majinnaibu.monstercards.data.enums | package com.majinnaibu.monstercards.data.enums; | ||||||
| 
 | 
 | ||||||
| enum class ArmorType( | @SuppressWarnings("unused") | ||||||
|     @JvmField val stringValue: String, | public enum ArmorType { | ||||||
|     @JvmField val displayName: String, |  | ||||||
|     @JvmField val baseArmorClass: Int |  | ||||||
| ) { |  | ||||||
|     NONE("none", "None", 10), |     NONE("none", "None", 10), | ||||||
|     NATURAL_ARMOR("natural armor", "Natural Armor", 10), |     NATURAL_ARMOR("natural armor", "Natural Armor", 10), | ||||||
|     MAGE_ARMOR("mage armor", "Mage Armor", 10), |     MAGE_ARMOR("mage armor", "Mage Armor", 10), | ||||||
| @@ -20,17 +17,25 @@ enum class ArmorType( | |||||||
|     CHAIN_MAIL("chain mail", "Chain Mail", 16), |     CHAIN_MAIL("chain mail", "Chain Mail", 16), | ||||||
|     SPLINT_MAIL("splint", "Splint Mail", 17), |     SPLINT_MAIL("splint", "Splint Mail", 17), | ||||||
|     PLATE_MAIL("plate", "Plate Mail", 18), |     PLATE_MAIL("plate", "Plate Mail", 18), | ||||||
|     OTHER("other", "Other", 10); |     OTHER("other", "Other", 10), | ||||||
|  |     ; | ||||||
| 
 | 
 | ||||||
|     companion object { |     public final String displayName; | ||||||
|         @JvmStatic |     public final String stringValue; | ||||||
|         fun valueOfString(string: String): ArmorType { |     public final int baseArmorClass; | ||||||
|             for (armorType in values()) { | 
 | ||||||
|                 if (armorType.stringValue == string) { |     ArmorType(String stringValue, String displayName, int baseArmorClass) { | ||||||
|                     return armorType |         this.displayName = displayName; | ||||||
|                 } |         this.stringValue = stringValue; | ||||||
|  |         this.baseArmorClass = baseArmorClass; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static ArmorType valueOfString(String string) { | ||||||
|  |         for (ArmorType armorType : values()) { | ||||||
|  |             if (armorType.stringValue.equals(string)) { | ||||||
|  |                 return armorType; | ||||||
|             } |             } | ||||||
|             return NONE |  | ||||||
|         } |         } | ||||||
|  |         return ArmorType.NONE; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -1,10 +1,7 @@ | |||||||
| package com.majinnaibu.monstercards.data.enums | package com.majinnaibu.monstercards.data.enums; | ||||||
| 
 | 
 | ||||||
| enum class ChallengeRating( | @SuppressWarnings("unused") | ||||||
|     @JvmField val stringValue: String, | public enum ChallengeRating { | ||||||
|     @JvmField val displayName: String, |  | ||||||
|     @JvmField val proficiencyBonus: Int |  | ||||||
| ) { |  | ||||||
|     CUSTOM("custom", "Custom", 0), |     CUSTOM("custom", "Custom", 0), | ||||||
|     ZERO("zero", "0 (10 XP)", 2), |     ZERO("zero", "0 (10 XP)", 2), | ||||||
|     ONE_EIGHTH("1/8", "1/8 (25 XP)", 2), |     ONE_EIGHTH("1/8", "1/8 (25 XP)", 2), | ||||||
| @@ -39,17 +36,25 @@ enum class ChallengeRating( | |||||||
|     TWENTY_SEVEN("27", "27 (105,000 XP)", 8), |     TWENTY_SEVEN("27", "27 (105,000 XP)", 8), | ||||||
|     TWENTY_EIGHT("28", "28 (120,000 XP)", 8), |     TWENTY_EIGHT("28", "28 (120,000 XP)", 8), | ||||||
|     TWENTY_NINE("29", "29 (135,000 XP)", 9), |     TWENTY_NINE("29", "29 (135,000 XP)", 9), | ||||||
|     THIRTY("30", "30 (155,000 XP)", 9); |     THIRTY("30", "30 (155,000 XP)", 9), | ||||||
|  |     ; | ||||||
| 
 | 
 | ||||||
|     companion object { |     public final String displayName; | ||||||
|         @JvmStatic |     public final String stringValue; | ||||||
|         fun valueOfString(string: String): ChallengeRating { |     public final int proficiencyBonus; | ||||||
|             for (challengeRating in values()) { | 
 | ||||||
|                 if (challengeRating.stringValue == string) { |     ChallengeRating(String stringValue, String displayName, int proficiencyBonus) { | ||||||
|                     return challengeRating |         this.displayName = displayName; | ||||||
|                 } |         this.stringValue = stringValue; | ||||||
|  |         this.proficiencyBonus = proficiencyBonus; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static ChallengeRating valueOfString(String string) { | ||||||
|  |         for (ChallengeRating challengeRating : values()) { | ||||||
|  |             if (challengeRating.stringValue.equals(string)) { | ||||||
|  |                 return challengeRating; | ||||||
|             } |             } | ||||||
|             return ONE |  | ||||||
|         } |         } | ||||||
|  |         return ChallengeRating.ONE; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -0,0 +1,27 @@ | |||||||
|  | package com.majinnaibu.monstercards.data.enums; | ||||||
|  |  | ||||||
|  | public enum ProficiencyType { | ||||||
|  |     NONE("none", "None", ""), | ||||||
|  |     PROFICIENT("proficient", "Proficient", "P"), | ||||||
|  |     EXPERTISE("expertise", "Expertise", "Ex"), | ||||||
|  |     ; | ||||||
|  |  | ||||||
|  |     public final String displayName; | ||||||
|  |     public final String stringValue; | ||||||
|  |     public final String label; | ||||||
|  |  | ||||||
|  |     ProficiencyType(String stringValue, String displayName, String label) { | ||||||
|  |         this.displayName = displayName; | ||||||
|  |         this.stringValue = stringValue; | ||||||
|  |         this.label = label; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static ProficiencyType valueOfString(String string) { | ||||||
|  |         for (ProficiencyType proficiencyType : values()) { | ||||||
|  |             if (proficiencyType.stringValue.equals(string)) { | ||||||
|  |                 return proficiencyType; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return ProficiencyType.NONE; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,23 +0,0 @@ | |||||||
| package com.majinnaibu.monstercards.data.enums |  | ||||||
|  |  | ||||||
| enum class ProficiencyType( |  | ||||||
|     @JvmField val stringValue: String, |  | ||||||
|     @JvmField val displayName: String, |  | ||||||
|     @JvmField val label: String |  | ||||||
| ) { |  | ||||||
|     NONE("none", "None", ""), |  | ||||||
|     PROFICIENT("proficient", "Proficient", "P"), |  | ||||||
|     EXPERTISE("expertise", "Expertise", "Ex"); |  | ||||||
|  |  | ||||||
|     companion object { |  | ||||||
|         @JvmStatic |  | ||||||
|         fun valueOfString(string: String): ProficiencyType { |  | ||||||
|             for (proficiencyType in values()) { |  | ||||||
|                 if (proficiencyType.stringValue == string) { |  | ||||||
|                     return proficiencyType |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             return NONE |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| package com.majinnaibu.monstercards.data.enums | package com.majinnaibu.monstercards.data.enums; | ||||||
| 
 | 
 | ||||||
| enum class StringType { | public enum StringType { | ||||||
|     CONDITION_IMMUNITY, |     CONDITION_IMMUNITY, | ||||||
|     DAMAGE_IMMUNITY, |     DAMAGE_IMMUNITY, | ||||||
|     DAMAGE_RESISTANCE, |     DAMAGE_RESISTANCE, | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| package com.majinnaibu.monstercards.data.enums | package com.majinnaibu.monstercards.data.enums; | ||||||
| 
 | 
 | ||||||
| enum class TraitType { | public enum TraitType { | ||||||
|     ABILITY, |     ABILITY, | ||||||
|     ACTION, |     ACTION, | ||||||
|     LAIR_ACTION, |     LAIR_ACTION, | ||||||
| @@ -21,6 +21,7 @@ import java.util.Arrays; | |||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.HashSet; | import java.util.HashSet; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  | import java.util.Objects; | ||||||
| import java.util.Set; | import java.util.Set; | ||||||
| import java.util.UUID; | import java.util.UUID; | ||||||
|  |  | ||||||
| @@ -268,6 +269,69 @@ public class Monster { | |||||||
|         regionalActions = new ArrayList<>(); |         regionalActions = new ArrayList<>(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public static boolean areItemsTheSame(@NonNull Monster oldItem, @NonNull Monster newItem) { | ||||||
|  |         return Objects.equals(oldItem.id, newItem.id); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static boolean areContentsTheSame(@NonNull Monster oldItem, @NonNull Monster newItem) { | ||||||
|  |         return Objects.equals(oldItem.abilities, newItem.abilities) && | ||||||
|  |                 Objects.equals(oldItem.actions, newItem.actions) && | ||||||
|  |                 Objects.equals(oldItem.alignment, newItem.alignment) && | ||||||
|  |                 Objects.equals(oldItem.armorType, newItem.armorType) && | ||||||
|  |                 Objects.equals(oldItem.burrowSpeed, newItem.burrowSpeed) && | ||||||
|  |                 Objects.equals(oldItem.canHover, newItem.canHover) && | ||||||
|  |                 Objects.equals(oldItem.challengeRating, newItem.challengeRating) && | ||||||
|  |                 Objects.equals(oldItem.charismaSavingThrowAdvantage, newItem.charismaSavingThrowAdvantage) && | ||||||
|  |                 Objects.equals(oldItem.charismaSavingThrowProficiency, newItem.charismaSavingThrowProficiency) && | ||||||
|  |                 Objects.equals(oldItem.charismaScore, newItem.charismaScore) && | ||||||
|  |                 Objects.equals(oldItem.climbSpeed, newItem.climbSpeed) && | ||||||
|  |                 Objects.equals(oldItem.conditionImmunities, newItem.conditionImmunities) && | ||||||
|  |                 Objects.equals(oldItem.constitutionSavingThrowAdvantage, newItem.constitutionSavingThrowAdvantage) && | ||||||
|  |                 Objects.equals(oldItem.constitutionSavingThrowProficiency, newItem.constitutionSavingThrowProficiency) && | ||||||
|  |                 Objects.equals(oldItem.constitutionScore, newItem.constitutionScore) && | ||||||
|  |                 Objects.equals(oldItem.customChallengeRatingDescription, newItem.customChallengeRatingDescription) && | ||||||
|  |                 Objects.equals(oldItem.customHPDescription, newItem.customHPDescription) && | ||||||
|  |                 Objects.equals(oldItem.customProficiencyBonus, newItem.customProficiencyBonus) && | ||||||
|  |                 Objects.equals(oldItem.customSpeedDescription, newItem.customSpeedDescription) && | ||||||
|  |                 Objects.equals(oldItem.damageImmunities, newItem.damageImmunities) && | ||||||
|  |                 Objects.equals(oldItem.damageResistances, newItem.damageResistances) && | ||||||
|  |                 Objects.equals(oldItem.damageVulnerabilities, newItem.damageVulnerabilities) && | ||||||
|  |                 Objects.equals(oldItem.dexteritySavingThrowAdvantage, newItem.dexteritySavingThrowAdvantage) && | ||||||
|  |                 Objects.equals(oldItem.dexteritySavingThrowProficiency, newItem.dexteritySavingThrowProficiency) && | ||||||
|  |                 Objects.equals(oldItem.dexterityScore, newItem.dexterityScore) && | ||||||
|  |                 Objects.equals(oldItem.flySpeed, newItem.flySpeed) && | ||||||
|  |                 Objects.equals(oldItem.hasCustomHP, newItem.hasCustomHP) && | ||||||
|  |                 Objects.equals(oldItem.hasCustomSpeed, newItem.hasCustomSpeed) && | ||||||
|  |                 Objects.equals(oldItem.hitDice, newItem.hitDice) && | ||||||
|  |                 Objects.equals(oldItem.intelligenceSavingThrowAdvantage, newItem.intelligenceSavingThrowAdvantage) && | ||||||
|  |                 Objects.equals(oldItem.intelligenceSavingThrowProficiency, newItem.intelligenceSavingThrowProficiency) && | ||||||
|  |                 Objects.equals(oldItem.intelligenceScore, newItem.intelligenceScore) && | ||||||
|  |                 Objects.equals(oldItem.lairActions, newItem.lairActions) && | ||||||
|  |                 Objects.equals(oldItem.languages, newItem.languages) && | ||||||
|  |                 Objects.equals(oldItem.legendaryActions, newItem.legendaryActions) && | ||||||
|  |                 Objects.equals(oldItem.name, newItem.name) && | ||||||
|  |                 Objects.equals(oldItem.naturalArmorBonus, newItem.naturalArmorBonus) && | ||||||
|  |                 Objects.equals(oldItem.otherArmorDescription, newItem.otherArmorDescription) && | ||||||
|  |                 Objects.equals(oldItem.reactions, newItem.reactions) && | ||||||
|  |                 Objects.equals(oldItem.regionalActions, newItem.regionalActions) && | ||||||
|  |                 Objects.equals(oldItem.senses, newItem.senses) && | ||||||
|  |                 Objects.equals(oldItem.shieldBonus, newItem.shieldBonus) && | ||||||
|  |                 Objects.equals(oldItem.size, newItem.size) && | ||||||
|  |                 Objects.equals(oldItem.skills, newItem.skills) && | ||||||
|  |                 Objects.equals(oldItem.strengthSavingThrowAdvantage, newItem.strengthSavingThrowAdvantage) && | ||||||
|  |                 Objects.equals(oldItem.strengthSavingThrowProficiency, newItem.strengthSavingThrowProficiency) && | ||||||
|  |                 Objects.equals(oldItem.strengthScore, newItem.strengthScore) && | ||||||
|  |                 Objects.equals(oldItem.subtype, newItem.subtype) && | ||||||
|  |                 Objects.equals(oldItem.swimSpeed, newItem.swimSpeed) && | ||||||
|  |                 Objects.equals(oldItem.telepathyRange, newItem.telepathyRange) && | ||||||
|  |                 Objects.equals(oldItem.type, newItem.type) && | ||||||
|  |                 Objects.equals(oldItem.understandsButDescription, newItem.understandsButDescription) && | ||||||
|  |                 Objects.equals(oldItem.wisdomSavingThrowAdvantage, newItem.wisdomSavingThrowAdvantage) && | ||||||
|  |                 Objects.equals(oldItem.wisdomSavingThrowProficiency, newItem.wisdomSavingThrowProficiency) && | ||||||
|  |                 Objects.equals(oldItem.wisdomScore, newItem.wisdomScore) && | ||||||
|  |                 Objects.equals(oldItem.walkSpeed, newItem.walkSpeed); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public String getMeta() { |     public String getMeta() { | ||||||
|         StringBuilder sb = new StringBuilder(); |         StringBuilder sb = new StringBuilder(); | ||||||
|         boolean isFirstOutput = true; |         boolean isFirstOutput = true; | ||||||
|   | |||||||
| @@ -21,14 +21,12 @@ import com.majinnaibu.monstercards.utils.Logger; | |||||||
|  |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; |  | ||||||
| import io.reactivex.rxjava3.schedulers.Schedulers; |  | ||||||
|  |  | ||||||
| public class DashboardFragment extends MCFragment { | public class DashboardFragment extends MCFragment { | ||||||
|     private DashboardViewModel mViewModel; |     private DashboardViewModel mViewModel; | ||||||
|     private ViewHolder mHolder; |     private ViewHolder mHolder; | ||||||
|     private DashboardRecyclerViewAdapter mAdapter; |     private DashboardRecyclerViewAdapter mAdapter; | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|     public View onCreateView(@NonNull LayoutInflater inflater, |     public View onCreateView(@NonNull LayoutInflater inflater, | ||||||
|                              ViewGroup container, Bundle savedInstanceState) { |                              ViewGroup container, Bundle savedInstanceState) { | ||||||
|         mViewModel = new ViewModelProvider(this).get(DashboardViewModel.class); |         mViewModel = new ViewModelProvider(this).get(DashboardViewModel.class); | ||||||
| @@ -37,13 +35,6 @@ public class DashboardFragment extends MCFragment { | |||||||
|  |  | ||||||
|         setupRecyclerView(mHolder.list); |         setupRecyclerView(mHolder.list); | ||||||
|  |  | ||||||
|         // TODO: subscribe better |  | ||||||
|         getMonsterRepository() |  | ||||||
|                 .getMonsters() |  | ||||||
|                 .subscribeOn(Schedulers.io()) |  | ||||||
|                 .observeOn(AndroidSchedulers.mainThread()) |  | ||||||
|                 .subscribe(monsters -> mViewModel.setMonsters(monsters)); |  | ||||||
|  |  | ||||||
|         return root; |         return root; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ import com.majinnaibu.monstercards.databinding.CardMonsterBinding; | |||||||
| import com.majinnaibu.monstercards.helpers.CommonMarkHelper; | import com.majinnaibu.monstercards.helpers.CommonMarkHelper; | ||||||
| import com.majinnaibu.monstercards.models.Monster; | import com.majinnaibu.monstercards.models.Monster; | ||||||
| import com.majinnaibu.monstercards.models.Trait; | import com.majinnaibu.monstercards.models.Trait; | ||||||
|  | import com.majinnaibu.monstercards.utils.ItemCallback; | ||||||
| import com.majinnaibu.monstercards.utils.Logger; | import com.majinnaibu.monstercards.utils.Logger; | ||||||
|  |  | ||||||
| import java.util.Locale; | import java.util.Locale; | ||||||
| @@ -28,17 +29,17 @@ public class DashboardRecyclerViewAdapter extends ListAdapter<Monster, Dashboard | |||||||
|     private static final DiffUtil.ItemCallback<Monster> DIFF_CALLBACK = new DiffUtil.ItemCallback<Monster>() { |     private static final DiffUtil.ItemCallback<Monster> DIFF_CALLBACK = new DiffUtil.ItemCallback<Monster>() { | ||||||
|         @Override |         @Override | ||||||
|         public boolean areItemsTheSame(@NonNull Monster oldItem, @NonNull Monster newItem) { |         public boolean areItemsTheSame(@NonNull Monster oldItem, @NonNull Monster newItem) { | ||||||
|             return oldItem.id.equals(newItem.id); |             return Monster.areItemsTheSame(oldItem, newItem); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         @Override |         @Override | ||||||
|         public boolean areContentsTheSame(@NonNull Monster oldItem, @NonNull Monster newItem) { |         public boolean areContentsTheSame(@NonNull Monster oldItem, @NonNull Monster newItem) { | ||||||
|             return oldItem.equals(newItem); |             return Monster.areContentsTheSame(oldItem, newItem); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     private final ItemCallback mOnClick; |     private final ItemCallback<Monster> mOnClick; | ||||||
|  |  | ||||||
|     protected DashboardRecyclerViewAdapter(ItemCallback onClick) { |     protected DashboardRecyclerViewAdapter(ItemCallback<Monster> onClick) { | ||||||
|         super(DIFF_CALLBACK); |         super(DIFF_CALLBACK); | ||||||
|         mOnClick = onClick; |         mOnClick = onClick; | ||||||
|     } |     } | ||||||
| @@ -51,7 +52,6 @@ public class DashboardRecyclerViewAdapter extends ListAdapter<Monster, Dashboard | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onBindViewHolder(@NonNull ViewHolder holder, int position) { |     public void onBindViewHolder(@NonNull ViewHolder holder, int position) { | ||||||
|         Logger.logUnimplementedMethod(); |  | ||||||
|         Monster monster = getItem(position); |         Monster monster = getItem(position); | ||||||
|         holder.monster = monster; |         holder.monster = monster; | ||||||
|         holder.name.setText(monster.name); |         holder.name.setText(monster.name); | ||||||
| @@ -120,15 +120,11 @@ public class DashboardRecyclerViewAdapter extends ListAdapter<Monster, Dashboard | |||||||
|  |  | ||||||
|         holder.itemView.setOnClickListener(v -> { |         holder.itemView.setOnClickListener(v -> { | ||||||
|             if (mOnClick != null) { |             if (mOnClick != null) { | ||||||
|                 mOnClick.onItemCallback(holder.monster); |                 mOnClick.onItem(holder.monster); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public interface ItemCallback { |  | ||||||
|         void onItemCallback(Monster monster); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public static class ViewHolder extends RecyclerView.ViewHolder { |     public static class ViewHolder extends RecyclerView.ViewHolder { | ||||||
|         public final TextView name; |         public final TextView name; | ||||||
|         public final TextView meta; |         public final TextView meta; | ||||||
| @@ -242,7 +238,6 @@ public class DashboardRecyclerViewAdapter extends ListAdapter<Monster, Dashboard | |||||||
|  |  | ||||||
|         @NonNull |         @NonNull | ||||||
|         public static String getChallengeRatingAbbreviation(@NonNull ChallengeRating challengeRating) { |         public static String getChallengeRatingAbbreviation(@NonNull ChallengeRating challengeRating) { | ||||||
|             Logger.logUnimplementedMethod(); |  | ||||||
|             switch (challengeRating) { |             switch (challengeRating) { | ||||||
|                 case CUSTOM: |                 case CUSTOM: | ||||||
|                     return "*"; |                     return "*"; | ||||||
|   | |||||||
| @@ -1,26 +1,50 @@ | |||||||
| package com.majinnaibu.monstercards.ui.dashboard; | package com.majinnaibu.monstercards.ui.dashboard; | ||||||
|  |  | ||||||
|  | import android.app.Application; | ||||||
|  |  | ||||||
|  | import androidx.lifecycle.AndroidViewModel; | ||||||
| import androidx.lifecycle.LiveData; | import androidx.lifecycle.LiveData; | ||||||
| import androidx.lifecycle.MutableLiveData; | import androidx.lifecycle.MutableLiveData; | ||||||
| import androidx.lifecycle.ViewModel; |  | ||||||
|  |  | ||||||
|  | import com.majinnaibu.monstercards.AppDatabase; | ||||||
| import com.majinnaibu.monstercards.models.Monster; | import com.majinnaibu.monstercards.models.Monster; | ||||||
|  |  | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| public class DashboardViewModel extends ViewModel { | import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; | ||||||
|  | import io.reactivex.rxjava3.schedulers.Schedulers; | ||||||
|  | import io.reactivex.rxjava3.subscribers.DisposableSubscriber; | ||||||
|  |  | ||||||
|  | public class DashboardViewModel extends AndroidViewModel { | ||||||
|  |     private final AppDatabase mDB; | ||||||
|     private final MutableLiveData<List<Monster>> mMonsters; |     private final MutableLiveData<List<Monster>> mMonsters; | ||||||
|  |  | ||||||
|     public DashboardViewModel() { |     public DashboardViewModel(Application application) { | ||||||
|  |         super(application); | ||||||
|  |         mDB = AppDatabase.getInstance(application); | ||||||
|         mMonsters = new MutableLiveData<>(new ArrayList<>()); |         mMonsters = new MutableLiveData<>(new ArrayList<>()); | ||||||
|  |         mDB.monsterDAO() | ||||||
|  |                 .getAll() | ||||||
|  |                 .subscribeOn(Schedulers.io()) | ||||||
|  |                 .observeOn(AndroidSchedulers.mainThread()) | ||||||
|  |                 .subscribe(new DisposableSubscriber<List<Monster>>() { | ||||||
|  |                     @Override | ||||||
|  |                     public void onNext(List<Monster> monsters) { | ||||||
|  |                         mMonsters.setValue(monsters); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     @Override | ||||||
|  |                     public void onError(Throwable t) { | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     @Override | ||||||
|  |                     public void onComplete() { | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public LiveData<List<Monster>> getMonsters() { |     public LiveData<List<Monster>> getMonsters() { | ||||||
|         return mMonsters; |         return mMonsters; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void setMonsters(List<Monster> monsters) { |  | ||||||
|         mMonsters.setValue(monsters); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,16 +10,16 @@ import androidx.annotation.NonNull; | |||||||
| import androidx.recyclerview.widget.RecyclerView; | import androidx.recyclerview.widget.RecyclerView; | ||||||
|  |  | ||||||
| import com.majinnaibu.monstercards.databinding.FragmentEditLanguagesListHeaderBinding; | import com.majinnaibu.monstercards.databinding.FragmentEditLanguagesListHeaderBinding; | ||||||
| import com.majinnaibu.monstercards.databinding.FragmentEditLanguagesListItemBinding; |  | ||||||
| import com.majinnaibu.monstercards.models.Language; | import com.majinnaibu.monstercards.models.Language; | ||||||
| import com.majinnaibu.monstercards.ui.components.Stepper; | import com.majinnaibu.monstercards.ui.components.Stepper; | ||||||
|  | import com.majinnaibu.monstercards.utils.ItemCallback; | ||||||
|  |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Locale; | import java.util.Locale; | ||||||
|  |  | ||||||
| public class EditLanguagesRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { | public class EditLanguagesRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { | ||||||
|     private final List<Language> mValues; |     private final List<Language> mValues; | ||||||
|     private final ItemCallback mOnClick; |     private final ItemCallback<Language> mOnClick; | ||||||
|     private final int mTelepathyRange; |     private final int mTelepathyRange; | ||||||
|     private final String mUnderstandsBut; |     private final String mUnderstandsBut; | ||||||
|     private final Stepper.OnValueChangeListener mOnTelepathyRangeChanged; |     private final Stepper.OnValueChangeListener mOnTelepathyRangeChanged; | ||||||
| @@ -29,7 +29,7 @@ public class EditLanguagesRecyclerViewAdapter extends RecyclerView.Adapter<Recyc | |||||||
|     private final int ITEM_VIEW_TYPE = 2; |     private final int ITEM_VIEW_TYPE = 2; | ||||||
|     private final String DISTANCE_IN_FEET_FORMAT = "%d ft."; |     private final String DISTANCE_IN_FEET_FORMAT = "%d ft."; | ||||||
|  |  | ||||||
|     public EditLanguagesRecyclerViewAdapter(List<Language> items, ItemCallback onClick, int telepathyRange, Stepper.OnValueChangeListener telepathyRangeChangedListener, String understandsBut, TextWatcher understandsButChangedListener) { |     public EditLanguagesRecyclerViewAdapter(List<Language> items, ItemCallback<Language> onClick, int telepathyRange, Stepper.OnValueChangeListener telepathyRangeChangedListener, String understandsBut, TextWatcher understandsButChangedListener) { | ||||||
|         mValues = items; |         mValues = items; | ||||||
|         mOnClick = onClick; |         mOnClick = onClick; | ||||||
|         mTelepathyRange = telepathyRange; |         mTelepathyRange = telepathyRange; | ||||||
| @@ -44,7 +44,7 @@ public class EditLanguagesRecyclerViewAdapter extends RecyclerView.Adapter<Recyc | |||||||
|         if (viewType == HEADER_VIEW_TYPE) { |         if (viewType == HEADER_VIEW_TYPE) { | ||||||
|             return new HeaderViewHolder(FragmentEditLanguagesListHeaderBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); |             return new HeaderViewHolder(FragmentEditLanguagesListHeaderBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); | ||||||
|         } |         } | ||||||
|         return new ItemViewHolder(FragmentEditLanguagesListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); |         return new ItemViewHolder(com.majinnaibu.monstercards.databinding.SimpleListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -62,7 +62,7 @@ public class EditLanguagesRecyclerViewAdapter extends RecyclerView.Adapter<Recyc | |||||||
|             itemViewHolder.mContentView.setText(itemViewHolder.mItem.getName()); |             itemViewHolder.mContentView.setText(itemViewHolder.mItem.getName()); | ||||||
|             itemViewHolder.itemView.setOnClickListener(view -> { |             itemViewHolder.itemView.setOnClickListener(view -> { | ||||||
|                 if (mOnClick != null) { |                 if (mOnClick != null) { | ||||||
|                     mOnClick.onItemCallback(itemViewHolder.mItem); |                     mOnClick.onItem(itemViewHolder.mItem); | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
| @@ -81,10 +81,6 @@ public class EditLanguagesRecyclerViewAdapter extends RecyclerView.Adapter<Recyc | |||||||
|         return ITEM_VIEW_TYPE; |         return ITEM_VIEW_TYPE; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public interface ItemCallback { |  | ||||||
|         void onItemCallback(Language language); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public static class HeaderViewHolder extends RecyclerView.ViewHolder { |     public static class HeaderViewHolder extends RecyclerView.ViewHolder { | ||||||
|         public final Stepper telepathy; |         public final Stepper telepathy; | ||||||
|         public final EditText understandsBut; |         public final EditText understandsBut; | ||||||
| @@ -100,7 +96,7 @@ public class EditLanguagesRecyclerViewAdapter extends RecyclerView.Adapter<Recyc | |||||||
|         public final TextView mContentView; |         public final TextView mContentView; | ||||||
|         public Language mItem; |         public Language mItem; | ||||||
|  |  | ||||||
|         public ItemViewHolder(@NonNull FragmentEditLanguagesListItemBinding binding) { |         public ItemViewHolder(@NonNull com.majinnaibu.monstercards.databinding.SimpleListItemBinding binding) { | ||||||
|             super(binding.getRoot()); |             super(binding.getRoot()); | ||||||
|             mContentView = binding.content; |             mContentView = binding.content; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -7,8 +7,8 @@ import android.widget.TextView; | |||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
| import androidx.recyclerview.widget.RecyclerView; | import androidx.recyclerview.widget.RecyclerView; | ||||||
|  |  | ||||||
| import com.majinnaibu.monstercards.databinding.FragmentEditSkillsListItemBinding; |  | ||||||
| import com.majinnaibu.monstercards.models.Skill; | import com.majinnaibu.monstercards.models.Skill; | ||||||
|  | import com.majinnaibu.monstercards.utils.ItemCallback; | ||||||
|  |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| @@ -17,9 +17,9 @@ import java.util.List; | |||||||
|  */ |  */ | ||||||
| public class EditSkillsRecyclerViewAdapter extends RecyclerView.Adapter<EditSkillsRecyclerViewAdapter.ViewHolder> { | public class EditSkillsRecyclerViewAdapter extends RecyclerView.Adapter<EditSkillsRecyclerViewAdapter.ViewHolder> { | ||||||
|     private final List<Skill> mValues; |     private final List<Skill> mValues; | ||||||
|     private final ItemCallback mOnClick; |     private final ItemCallback<Skill> mOnClick; | ||||||
|  |  | ||||||
|     public EditSkillsRecyclerViewAdapter(List<Skill> items, ItemCallback onClick) { |     public EditSkillsRecyclerViewAdapter(List<Skill> items, ItemCallback<Skill> onClick) { | ||||||
|         mValues = items; |         mValues = items; | ||||||
|         mOnClick = onClick; |         mOnClick = onClick; | ||||||
|     } |     } | ||||||
| @@ -27,7 +27,7 @@ public class EditSkillsRecyclerViewAdapter extends RecyclerView.Adapter<EditSkil | |||||||
|     @NonNull |     @NonNull | ||||||
|     @Override |     @Override | ||||||
|     public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { |     public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | ||||||
|         return new ViewHolder(FragmentEditSkillsListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); |         return new ViewHolder(com.majinnaibu.monstercards.databinding.SimpleListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -36,7 +36,7 @@ public class EditSkillsRecyclerViewAdapter extends RecyclerView.Adapter<EditSkil | |||||||
|         holder.mContentView.setText(mValues.get(position).name); |         holder.mContentView.setText(mValues.get(position).name); | ||||||
|         holder.itemView.setOnClickListener(v -> { |         holder.itemView.setOnClickListener(v -> { | ||||||
|             if (mOnClick != null) { |             if (mOnClick != null) { | ||||||
|                 mOnClick.onItemCallback(holder.mItem); |                 mOnClick.onItem(holder.mItem); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @@ -46,15 +46,11 @@ public class EditSkillsRecyclerViewAdapter extends RecyclerView.Adapter<EditSkil | |||||||
|         return mValues.size(); |         return mValues.size(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public interface ItemCallback { |  | ||||||
|         void onItemCallback(Skill skill); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public static class ViewHolder extends RecyclerView.ViewHolder { |     public static class ViewHolder extends RecyclerView.ViewHolder { | ||||||
|         public final TextView mContentView; |         public final TextView mContentView; | ||||||
|         public Skill mItem; |         public Skill mItem; | ||||||
|  |  | ||||||
|         public ViewHolder(@NonNull FragmentEditSkillsListItemBinding binding) { |         public ViewHolder(@NonNull com.majinnaibu.monstercards.databinding.SimpleListItemBinding binding) { | ||||||
|             super(binding.getRoot()); |             super(binding.getRoot()); | ||||||
|             mContentView = binding.content; |             mContentView = binding.content; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -7,15 +7,16 @@ import android.widget.TextView; | |||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
| import androidx.recyclerview.widget.RecyclerView; | import androidx.recyclerview.widget.RecyclerView; | ||||||
|  |  | ||||||
| import com.majinnaibu.monstercards.databinding.FragmentEditStringsListItemBinding; | import com.majinnaibu.monstercards.databinding.SimpleListItemBinding; | ||||||
|  | import com.majinnaibu.monstercards.utils.ItemCallback; | ||||||
|  |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| public class EditStringsRecyclerViewAdapter extends RecyclerView.Adapter<EditStringsRecyclerViewAdapter.ViewHolder> { | public class EditStringsRecyclerViewAdapter extends RecyclerView.Adapter<EditStringsRecyclerViewAdapter.ViewHolder> { | ||||||
|     private final List<String> mValues; |     private final List<String> mValues; | ||||||
|     private final ItemCallback mOnClick; |     private final ItemCallback<String> mOnClick; | ||||||
|  |  | ||||||
|     public EditStringsRecyclerViewAdapter(List<String> items, ItemCallback onClick) { |     public EditStringsRecyclerViewAdapter(List<String> items, ItemCallback<String> onClick) { | ||||||
|         mValues = items; |         mValues = items; | ||||||
|         mOnClick = onClick; |         mOnClick = onClick; | ||||||
|     } |     } | ||||||
| @@ -23,7 +24,7 @@ public class EditStringsRecyclerViewAdapter extends RecyclerView.Adapter<EditStr | |||||||
|     @NonNull |     @NonNull | ||||||
|     @Override |     @Override | ||||||
|     public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { |     public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | ||||||
|         return new ViewHolder(FragmentEditStringsListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); |         return new ViewHolder(SimpleListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -32,7 +33,7 @@ public class EditStringsRecyclerViewAdapter extends RecyclerView.Adapter<EditStr | |||||||
|         holder.mContentView.setText(mValues.get(position)); |         holder.mContentView.setText(mValues.get(position)); | ||||||
|         holder.itemView.setOnClickListener(v -> { |         holder.itemView.setOnClickListener(v -> { | ||||||
|             if (mOnClick != null) { |             if (mOnClick != null) { | ||||||
|                 mOnClick.onItemCallback(holder.mItem); |                 mOnClick.onItem(holder.mItem); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @@ -42,15 +43,11 @@ public class EditStringsRecyclerViewAdapter extends RecyclerView.Adapter<EditStr | |||||||
|         return mValues.size(); |         return mValues.size(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public interface ItemCallback { |  | ||||||
|         void onItemCallback(String value); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public static class ViewHolder extends RecyclerView.ViewHolder { |     public static class ViewHolder extends RecyclerView.ViewHolder { | ||||||
|         public final TextView mContentView; |         public final TextView mContentView; | ||||||
|         public String mItem; |         public String mItem; | ||||||
|  |  | ||||||
|         public ViewHolder(@NonNull FragmentEditStringsListItemBinding binding) { |         public ViewHolder(@NonNull SimpleListItemBinding binding) { | ||||||
|             super(binding.getRoot()); |             super(binding.getRoot()); | ||||||
|             mContentView = binding.content; |             mContentView = binding.content; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -35,7 +35,6 @@ public class EditTraitsFragment extends MCFragment { | |||||||
|     private TraitType mTraitType; |     private TraitType mTraitType; | ||||||
|     private EditTraitsRecyclerViewAdapter mAdapter; |     private EditTraitsRecyclerViewAdapter mAdapter; | ||||||
|  |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onCreate(@Nullable Bundle savedInstanceState) { |     public void onCreate(@Nullable Bundle savedInstanceState) { | ||||||
|         if (getArguments() != null) { |         if (getArguments() != null) { | ||||||
| @@ -47,9 +46,8 @@ public class EditTraitsFragment extends MCFragment { | |||||||
|         super.onCreate(savedInstanceState); |         super.onCreate(savedInstanceState); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Nullable |  | ||||||
|     @Override |     @Override | ||||||
|     public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { |     public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { | ||||||
|         NavController navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment); |         NavController navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment); | ||||||
|         NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.edit_monster_navigation); |         NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.edit_monster_navigation); | ||||||
|         mViewModel = new ViewModelProvider(backStackEntry).get(EditMonsterViewModel.class); |         mViewModel = new ViewModelProvider(backStackEntry).get(EditMonsterViewModel.class); | ||||||
|   | |||||||
| @@ -9,8 +9,9 @@ import androidx.recyclerview.widget.DiffUtil; | |||||||
| import androidx.recyclerview.widget.ListAdapter; | import androidx.recyclerview.widget.ListAdapter; | ||||||
| import androidx.recyclerview.widget.RecyclerView; | import androidx.recyclerview.widget.RecyclerView; | ||||||
|  |  | ||||||
| import com.majinnaibu.monstercards.databinding.FragmentEditTraitsListItemBinding; | import com.majinnaibu.monstercards.databinding.SimpleListItemBinding; | ||||||
| import com.majinnaibu.monstercards.models.Trait; | import com.majinnaibu.monstercards.models.Trait; | ||||||
|  | import com.majinnaibu.monstercards.utils.ItemCallback; | ||||||
|  |  | ||||||
| public class EditTraitsRecyclerViewAdapter extends ListAdapter<Trait, EditTraitsRecyclerViewAdapter.ViewHolder> { | public class EditTraitsRecyclerViewAdapter extends ListAdapter<Trait, EditTraitsRecyclerViewAdapter.ViewHolder> { | ||||||
|     private static final DiffUtil.ItemCallback<Trait> DIFF_CALLBACK = new DiffUtil.ItemCallback<Trait>() { |     private static final DiffUtil.ItemCallback<Trait> DIFF_CALLBACK = new DiffUtil.ItemCallback<Trait>() { | ||||||
| @@ -25,9 +26,9 @@ public class EditTraitsRecyclerViewAdapter extends ListAdapter<Trait, EditTraits | |||||||
|             return oldItem.equals(newItem); |             return oldItem.equals(newItem); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     private final ItemCallback mOnClick; |     private final ItemCallback<Trait> mOnClick; | ||||||
|  |  | ||||||
|     protected EditTraitsRecyclerViewAdapter(ItemCallback onClick) { |     protected EditTraitsRecyclerViewAdapter(ItemCallback<Trait> onClick) { | ||||||
|         super(DIFF_CALLBACK); |         super(DIFF_CALLBACK); | ||||||
|         mOnClick = onClick; |         mOnClick = onClick; | ||||||
|     } |     } | ||||||
| @@ -35,7 +36,7 @@ public class EditTraitsRecyclerViewAdapter extends ListAdapter<Trait, EditTraits | |||||||
|     @NonNull |     @NonNull | ||||||
|     @Override |     @Override | ||||||
|     public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { |     public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | ||||||
|         return new ViewHolder(FragmentEditTraitsListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); |         return new ViewHolder(SimpleListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -44,20 +45,16 @@ public class EditTraitsRecyclerViewAdapter extends ListAdapter<Trait, EditTraits | |||||||
|         holder.mContentView.setText(holder.mItem.name); |         holder.mContentView.setText(holder.mItem.name); | ||||||
|         holder.itemView.setOnClickListener(v -> { |         holder.itemView.setOnClickListener(v -> { | ||||||
|             if (mOnClick != null) { |             if (mOnClick != null) { | ||||||
|                 mOnClick.onItemCallback(holder.mItem); |                 mOnClick.onItem(holder.mItem); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public interface ItemCallback { |  | ||||||
|         void onItemCallback(Trait trait); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public static class ViewHolder extends RecyclerView.ViewHolder { |     public static class ViewHolder extends RecyclerView.ViewHolder { | ||||||
|         public final TextView mContentView; |         public final TextView mContentView; | ||||||
|         public Trait mItem; |         public Trait mItem; | ||||||
|  |  | ||||||
|         public ViewHolder(@NonNull FragmentEditTraitsListItemBinding binding) { |         public ViewHolder(@NonNull SimpleListItemBinding binding) { | ||||||
|             super(binding.getRoot()); |             super(binding.getRoot()); | ||||||
|             mContentView = binding.content; |             mContentView = binding.content; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -7,6 +7,8 @@ import android.view.View; | |||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
|  |  | ||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
|  | import androidx.lifecycle.LiveData; | ||||||
|  | import androidx.lifecycle.ViewModelProvider; | ||||||
| import androidx.navigation.NavDirections; | import androidx.navigation.NavDirections; | ||||||
| import androidx.navigation.Navigation; | import androidx.navigation.Navigation; | ||||||
| import androidx.recyclerview.widget.DividerItemDecoration; | import androidx.recyclerview.widget.DividerItemDecoration; | ||||||
| @@ -18,65 +20,52 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton; | |||||||
| import com.google.android.material.snackbar.Snackbar; | import com.google.android.material.snackbar.Snackbar; | ||||||
| import com.majinnaibu.monstercards.R; | import com.majinnaibu.monstercards.R; | ||||||
| import com.majinnaibu.monstercards.data.MonsterRepository; | import com.majinnaibu.monstercards.data.MonsterRepository; | ||||||
|  | import com.majinnaibu.monstercards.databinding.FragmentLibraryBinding; | ||||||
| import com.majinnaibu.monstercards.models.Monster; | import com.majinnaibu.monstercards.models.Monster; | ||||||
| import com.majinnaibu.monstercards.ui.shared.MCFragment; | import com.majinnaibu.monstercards.ui.shared.MCFragment; | ||||||
| import com.majinnaibu.monstercards.ui.shared.SwipeToDeleteCallback; | import com.majinnaibu.monstercards.ui.shared.SwipeToDeleteCallback; | ||||||
| import com.majinnaibu.monstercards.utils.Logger; | import com.majinnaibu.monstercards.utils.Logger; | ||||||
|  |  | ||||||
| import java.util.UUID; | import java.util.List; | ||||||
|  |  | ||||||
| import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; | import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; | ||||||
| import io.reactivex.rxjava3.observers.DisposableCompletableObserver; | import io.reactivex.rxjava3.observers.DisposableCompletableObserver; | ||||||
| import io.reactivex.rxjava3.schedulers.Schedulers; | import io.reactivex.rxjava3.schedulers.Schedulers; | ||||||
|  |  | ||||||
| public class LibraryFragment extends MCFragment { | public class LibraryFragment extends MCFragment { | ||||||
|  |     private LibraryViewModel mViewModel; | ||||||
|  |     private ViewHolder mHolder; | ||||||
|  |     private LibraryRecyclerViewAdapter mAdapter; | ||||||
|  |  | ||||||
|     public View onCreateView(@NonNull LayoutInflater inflater, |     @Override | ||||||
|                              ViewGroup container, Bundle savedInstanceState) { |     public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { | ||||||
|         View root = inflater.inflate(R.layout.fragment_library, container, false); |         mViewModel = new ViewModelProvider(this).get(LibraryViewModel.class); | ||||||
|  |         FragmentLibraryBinding binding = FragmentLibraryBinding.inflate(inflater, container, false); | ||||||
|         FloatingActionButton fab = root.findViewById(R.id.fab); |         mHolder = new ViewHolder(binding); | ||||||
|         assert fab != null; |         // TODO: set the title with setTitle(...) | ||||||
|         setupAddMonsterButton(fab); |         setupAddMonsterButton(mHolder.addButton); | ||||||
|  |         setupMonsterList(mHolder.list); | ||||||
|         final RecyclerView recyclerView = root.findViewById(R.id.monster_list); |         return binding.getRoot(); | ||||||
|         assert recyclerView != null; |  | ||||||
|         setupRecyclerView(recyclerView); |  | ||||||
|  |  | ||||||
|         return root; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void setupRecyclerView(@NonNull RecyclerView recyclerView) { |     private void setupMonsterList(@NonNull RecyclerView recyclerView) { | ||||||
|         Context context = requireContext(); |         Context context = requireContext(); | ||||||
|         MonsterRepository repository = this.getMonsterRepository(); |  | ||||||
|  |  | ||||||
|         LibraryRecyclerViewAdapter adapter = new LibraryRecyclerViewAdapter( |  | ||||||
|                 context, |  | ||||||
|                 repository.getMonsters(), |  | ||||||
|                 (monster) -> navigateToMonsterDetail(monster.id), |  | ||||||
|                 (monster) -> repository |  | ||||||
|                         .deleteMonster(monster) |  | ||||||
|                         .subscribeOn(Schedulers.io()) |  | ||||||
|                         .observeOn(AndroidSchedulers.mainThread()) |  | ||||||
|                         .subscribe(new DisposableCompletableObserver() { |  | ||||||
|                             @Override |  | ||||||
|                             public void onComplete() { |  | ||||||
|                             } |  | ||||||
|  |  | ||||||
|                             @Override |  | ||||||
|                             public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) { |  | ||||||
|                                 Logger.logError(e); |  | ||||||
|                             } |  | ||||||
|                         })); |  | ||||||
|         recyclerView.setAdapter(adapter); |  | ||||||
|  |  | ||||||
|         LinearLayoutManager layoutManager = new LinearLayoutManager(context); |         LinearLayoutManager layoutManager = new LinearLayoutManager(context); | ||||||
|         recyclerView.setLayoutManager(layoutManager); |         recyclerView.setLayoutManager(layoutManager); | ||||||
|  |  | ||||||
|  |         LiveData<List<Monster>> monsterData = mViewModel.getMonsters(); | ||||||
|  |         mAdapter = new LibraryRecyclerViewAdapter(this::navigateToMonsterDetail); | ||||||
|  |         if (monsterData != null) { | ||||||
|  |             monsterData.observe(getViewLifecycleOwner(), monsters -> mAdapter.submitList(monsters)); | ||||||
|  |         } | ||||||
|  |         recyclerView.setAdapter(mAdapter); | ||||||
|         DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation()); |         DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation()); | ||||||
|         recyclerView.addItemDecoration(dividerItemDecoration); |         recyclerView.addItemDecoration(dividerItemDecoration); | ||||||
|  |  | ||||||
|         ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(requireContext(), (position, direction) -> adapter.deleteItem(position), null)); |         ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback( | ||||||
|  |                 requireContext(), | ||||||
|  |                 (position, direction) -> mViewModel.removeMonster(position), | ||||||
|  |                 null)); | ||||||
|         itemTouchHelper.attachToRecyclerView(recyclerView); |         itemTouchHelper.attachToRecyclerView(recyclerView); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -98,7 +87,7 @@ public class LibraryFragment extends MCFragment { | |||||||
|                                             view, |                                             view, | ||||||
|                                             getString(R.string.snackbar_monster_created, monster.name), |                                             getString(R.string.snackbar_monster_created, monster.name), | ||||||
|                                             Snackbar.LENGTH_LONG) |                                             Snackbar.LENGTH_LONG) | ||||||
|                                             .setAction("Action", (_view) -> navigateToMonsterDetail(monster.id)) |                                             .setAction("Action", (_view) -> navigateToMonsterDetail(monster)) | ||||||
|                                             .show(); |                                             .show(); | ||||||
|                                 } |                                 } | ||||||
|  |  | ||||||
| @@ -114,8 +103,22 @@ public class LibraryFragment extends MCFragment { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected void navigateToMonsterDetail(@NonNull UUID monsterId) { |     protected void navigateToMonsterDetail(Monster monster) { | ||||||
|         NavDirections action = LibraryFragmentDirections.actionNavigationLibraryToNavigationMonster(monsterId.toString()); |         if (monster != null) { | ||||||
|         Navigation.findNavController(requireView()).navigate(action); |             NavDirections action = LibraryFragmentDirections.actionNavigationLibraryToNavigationMonster(monster.id.toString()); | ||||||
|  |             Navigation.findNavController(requireView()).navigate(action); | ||||||
|  |         } else { | ||||||
|  |             Logger.logError("Can't navigate to MonsterDetail without a monster."); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static class ViewHolder { | ||||||
|  |         final FloatingActionButton addButton; | ||||||
|  |         final RecyclerView list; | ||||||
|  |  | ||||||
|  |         public ViewHolder(FragmentLibraryBinding binding) { | ||||||
|  |             addButton = binding.fab; | ||||||
|  |             list = binding.monsterList; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,115 +1,53 @@ | |||||||
| package com.majinnaibu.monstercards.ui.library; | package com.majinnaibu.monstercards.ui.library; | ||||||
|  |  | ||||||
| import android.content.Context; |  | ||||||
| import android.view.LayoutInflater; | import android.view.LayoutInflater; | ||||||
| import android.view.View; |  | ||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
| import android.widget.TextView; |  | ||||||
|  |  | ||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
| import androidx.recyclerview.widget.RecyclerView; | import androidx.recyclerview.widget.DiffUtil; | ||||||
|  | import androidx.recyclerview.widget.ListAdapter; | ||||||
|  |  | ||||||
| import com.majinnaibu.monstercards.R; | import com.majinnaibu.monstercards.databinding.SimpleListItemBinding; | ||||||
| import com.majinnaibu.monstercards.models.Monster; | import com.majinnaibu.monstercards.models.Monster; | ||||||
|  | import com.majinnaibu.monstercards.ui.shared.SimpleListItemViewHolder; | ||||||
|  | import com.majinnaibu.monstercards.utils.ItemCallback; | ||||||
|  |  | ||||||
| import java.util.ArrayList; | public class LibraryRecyclerViewAdapter extends ListAdapter<Monster, SimpleListItemViewHolder<Monster>> { | ||||||
| import java.util.List; |     private static final DiffUtil.ItemCallback<Monster> DIFF_CALLBACK = new DiffUtil.ItemCallback<Monster>() { | ||||||
|  |  | ||||||
| import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; |  | ||||||
| import io.reactivex.rxjava3.core.Flowable; |  | ||||||
| import io.reactivex.rxjava3.disposables.Disposable; |  | ||||||
| import io.reactivex.rxjava3.schedulers.Schedulers; |  | ||||||
|  |  | ||||||
| public class LibraryRecyclerViewAdapter extends RecyclerView.Adapter<LibraryRecyclerViewAdapter.ViewHolder> { |  | ||||||
|     private final Context mContext; |  | ||||||
|     private final ItemCallback mOnDelete; |  | ||||||
|     private final ItemCallback mOnClick; |  | ||||||
|     private final Flowable<List<Monster>> mItemsObservable; |  | ||||||
|     private final View.OnClickListener mOnClickListener = new View.OnClickListener() { |  | ||||||
|         @Override |         @Override | ||||||
|         public void onClick(@NonNull View view) { |         public boolean areItemsTheSame(@NonNull Monster oldItem, @NonNull Monster newItem) { | ||||||
|             Monster monster = (Monster) view.getTag(); |             return Monster.areItemsTheSame(oldItem, newItem); | ||||||
|             if (mOnClick != null) { |         } | ||||||
|                 mOnClick.onItemCallback(monster); |  | ||||||
|             } |         @Override | ||||||
|  |         public boolean areContentsTheSame(@NonNull Monster oldItem, @NonNull Monster newItem) { | ||||||
|  |             return Monster.areContentsTheSame(oldItem, newItem); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     private List<Monster> mValues; |     private final ItemCallback<Monster> mOnClick; | ||||||
|     private Disposable mDisposable; |  | ||||||
|  |  | ||||||
|     public LibraryRecyclerViewAdapter(Context context, |     public LibraryRecyclerViewAdapter(ItemCallback<Monster> onClick) { | ||||||
|                                       Flowable<List<Monster>> itemsObservable, |         super(DIFF_CALLBACK); | ||||||
|                                       ItemCallback onClick, |  | ||||||
|                                       ItemCallback onDelete) { |  | ||||||
|         mItemsObservable = itemsObservable; |  | ||||||
|         mValues = new ArrayList<>(); |  | ||||||
|         mContext = context; |  | ||||||
|         mOnDelete = onDelete; |  | ||||||
|         mOnClick = onClick; |         mOnClick = onClick; | ||||||
|         mDisposable = null; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     @NonNull |     @NonNull | ||||||
|     public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { |     public SimpleListItemViewHolder<Monster> onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | ||||||
|         View view = LayoutInflater.from(parent.getContext()) |         SimpleListItemBinding binding = SimpleListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); | ||||||
|                 .inflate(R.layout.monster_list_content, parent, false); |         return new SimpleListItemViewHolder<>(binding); | ||||||
|         return new ViewHolder(view); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onBindViewHolder(final @NonNull ViewHolder holder, int position) { |     public void onBindViewHolder(final @NonNull SimpleListItemViewHolder<Monster> holder, int position) { | ||||||
|         Monster monster = mValues.get(position); |         Monster monster = getItem(position); | ||||||
|         holder.mContentView.setText(monster.name); |         holder.item = monster; | ||||||
|  |         holder.contentView.setText(monster.name); | ||||||
|         holder.itemView.setTag(monster); |         holder.itemView.setTag(monster); | ||||||
|         holder.itemView.setOnClickListener(mOnClickListener); |         holder.itemView.setOnClickListener(v -> { | ||||||
|     } |             if (mOnClick != null) { | ||||||
|  |                 mOnClick.onItem(holder.item); | ||||||
|     @Override |             } | ||||||
|     public int getItemCount() { |         }); | ||||||
|         return mValues.size(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public Context getContext() { |  | ||||||
|         return mContext; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { |  | ||||||
|         super.onAttachedToRecyclerView(recyclerView); |  | ||||||
|         // TODO: consider moving this subscription out of the adapter and make the subscriber call setItems on the adapter |  | ||||||
|         mDisposable = mItemsObservable |  | ||||||
|                 .subscribeOn(Schedulers.io()) |  | ||||||
|                 .observeOn(AndroidSchedulers.mainThread()) |  | ||||||
|                 .subscribe(monsters -> { |  | ||||||
|                     mValues = monsters; |  | ||||||
|                     notifyDataSetChanged(); |  | ||||||
|                 }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) { |  | ||||||
|         super.onDetachedFromRecyclerView(recyclerView); |  | ||||||
|         mDisposable.dispose(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void deleteItem(int position) { |  | ||||||
|         if (mOnDelete != null) { |  | ||||||
|             Monster monster = mValues.get(position); |  | ||||||
|             mOnDelete.onItemCallback(monster); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public interface ItemCallback { |  | ||||||
|         void onItemCallback(Monster monster); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static class ViewHolder extends RecyclerView.ViewHolder { |  | ||||||
|         final TextView mContentView; |  | ||||||
|  |  | ||||||
|         ViewHolder(View view) { |  | ||||||
|             super(view); |  | ||||||
|             mContentView = view.findViewById(R.id.content); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,74 @@ | |||||||
|  | package com.majinnaibu.monstercards.ui.library; | ||||||
|  |  | ||||||
|  | import android.app.Application; | ||||||
|  |  | ||||||
|  | import androidx.lifecycle.AndroidViewModel; | ||||||
|  | import androidx.lifecycle.LiveData; | ||||||
|  | import androidx.lifecycle.MutableLiveData; | ||||||
|  |  | ||||||
|  | import com.majinnaibu.monstercards.AppDatabase; | ||||||
|  | import com.majinnaibu.monstercards.models.Monster; | ||||||
|  |  | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; | ||||||
|  | import io.reactivex.rxjava3.annotations.NonNull; | ||||||
|  | import io.reactivex.rxjava3.observers.DisposableCompletableObserver; | ||||||
|  | import io.reactivex.rxjava3.schedulers.Schedulers; | ||||||
|  | import io.reactivex.rxjava3.subscribers.DisposableSubscriber; | ||||||
|  |  | ||||||
|  | public class LibraryViewModel extends AndroidViewModel { | ||||||
|  |     private final AppDatabase mDB; | ||||||
|  |     private final MutableLiveData<List<Monster>> mMonsters; | ||||||
|  |  | ||||||
|  |     public LibraryViewModel(Application application) { | ||||||
|  |         super(application); | ||||||
|  |         mDB = AppDatabase.getInstance(application); | ||||||
|  |         mMonsters = new MutableLiveData<>(new ArrayList<>()); | ||||||
|  |         mDB.monsterDAO() | ||||||
|  |                 .getAll() | ||||||
|  |                 .observeOn(AndroidSchedulers.mainThread()) | ||||||
|  |                 .subscribeOn(Schedulers.io()) | ||||||
|  |                 .subscribe(new DisposableSubscriber<List<Monster>>() { | ||||||
|  |                     @Override | ||||||
|  |                     public void onNext(List<Monster> monsters) { | ||||||
|  |                         mMonsters.setValue(monsters); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     @Override | ||||||
|  |                     public void onError(Throwable t) { | ||||||
|  |  | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     @Override | ||||||
|  |                     public void onComplete() { | ||||||
|  |  | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     public LiveData<List<Monster>> getMonsters() { | ||||||
|  |         return mMonsters; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void removeMonster(int position) { | ||||||
|  |         Monster monster = mMonsters.getValue().get(position); | ||||||
|  |         mDB.monsterDAO() | ||||||
|  |                 .delete(monster) | ||||||
|  |                 .observeOn(AndroidSchedulers.mainThread()) | ||||||
|  |                 .subscribeOn(Schedulers.io()) | ||||||
|  |                 .subscribe(new DisposableCompletableObserver() { | ||||||
|  |                     @Override | ||||||
|  |                     public void onComplete() { | ||||||
|  |  | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     @Override | ||||||
|  |                     public void onError(@NonNull Throwable e) { | ||||||
|  |  | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,34 +1,49 @@ | |||||||
| package com.majinnaibu.monstercards.ui.search; | package com.majinnaibu.monstercards.ui.search; | ||||||
|  |  | ||||||
|  | import android.content.Context; | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| import android.text.Editable; | import android.text.Editable; | ||||||
| import android.text.TextWatcher; | import android.text.TextWatcher; | ||||||
| import android.view.LayoutInflater; | import android.view.LayoutInflater; | ||||||
| import android.view.View; | import android.view.View; | ||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
|  | import android.widget.EditText; | ||||||
| import android.widget.TextView; | import android.widget.TextView; | ||||||
|  |  | ||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
|  | import androidx.lifecycle.LiveData; | ||||||
|  | import androidx.lifecycle.ViewModelProvider; | ||||||
|  | import androidx.navigation.NavDirections; | ||||||
|  | import androidx.navigation.Navigation; | ||||||
|  | import androidx.recyclerview.widget.DividerItemDecoration; | ||||||
| import androidx.recyclerview.widget.LinearLayoutManager; | import androidx.recyclerview.widget.LinearLayoutManager; | ||||||
| import androidx.recyclerview.widget.RecyclerView; | import androidx.recyclerview.widget.RecyclerView; | ||||||
|  |  | ||||||
| import com.majinnaibu.monstercards.R; | import com.majinnaibu.monstercards.databinding.FragmentSearchBinding; | ||||||
| import com.majinnaibu.monstercards.data.MonsterRepository; | import com.majinnaibu.monstercards.models.Monster; | ||||||
| import com.majinnaibu.monstercards.ui.shared.MCFragment; | import com.majinnaibu.monstercards.ui.shared.MCFragment; | ||||||
|  | import com.majinnaibu.monstercards.utils.Logger; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
| public class SearchFragment extends MCFragment { | public class SearchFragment extends MCFragment { | ||||||
|  |     private SearchViewModel mViewModel; | ||||||
|  |     private ViewHolder mHolder; | ||||||
|  |     private SearchResultsRecyclerViewAdapter mAdapter; | ||||||
|  |  | ||||||
|     public View onCreateView(@NonNull LayoutInflater inflater, |     @Override | ||||||
|                              ViewGroup container, Bundle savedInstanceState) { |     public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { | ||||||
|         View root = inflater.inflate(R.layout.fragment_search, container, false); |         mViewModel = new ViewModelProvider(this).get(SearchViewModel.class); | ||||||
|         MonsterRepository repository = this.getMonsterRepository(); |         FragmentSearchBinding binding = FragmentSearchBinding.inflate(inflater, container, false); | ||||||
|         SearchResultsRecyclerViewAdapter adapter = new SearchResultsRecyclerViewAdapter(repository, null); |         mHolder = new ViewHolder(binding); | ||||||
|         final RecyclerView recyclerView = root.findViewById(R.id.monster_list); |         // TODO: set the title with setTitle(...) | ||||||
|         assert recyclerView != null; |         setupMonsterList(binding.monsterList); | ||||||
|         setupRecyclerView(recyclerView, adapter); |         setupFilterBox(binding.searchQuery); | ||||||
|  |         return binding.getRoot(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|         final TextView textView = root.findViewById(R.id.search_query); |     private void setupFilterBox(@NonNull TextView textBox) { | ||||||
|         textView.addTextChangedListener(new TextWatcher() { |         textBox.addTextChangedListener(new TextWatcher() { | ||||||
|             @Override |             @Override | ||||||
|             public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { |             public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { | ||||||
|             } |             } | ||||||
| @@ -39,15 +54,42 @@ public class SearchFragment extends MCFragment { | |||||||
|  |  | ||||||
|             @Override |             @Override | ||||||
|             public void afterTextChanged(Editable editable) { |             public void afterTextChanged(Editable editable) { | ||||||
|                 adapter.doSearch(textView.getText().toString()); |                 mViewModel.setFilterText(textBox.getText().toString()); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         return root; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void setupRecyclerView(@NonNull RecyclerView recyclerView, @NonNull SearchResultsRecyclerViewAdapter adapter) { |     private void setupMonsterList(@NonNull RecyclerView recyclerView) { | ||||||
|         recyclerView.setAdapter(adapter); |         Context context = requireContext(); | ||||||
|         recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); |         LinearLayoutManager layoutManager = new LinearLayoutManager(context); | ||||||
|  |         recyclerView.setLayoutManager(layoutManager); | ||||||
|  |  | ||||||
|  |         LiveData<List<Monster>> monsterData = mViewModel.getMatchedMonsters(); | ||||||
|  |         mAdapter = new SearchResultsRecyclerViewAdapter(this::navigateToMonsterDetail); | ||||||
|  |         if (monsterData != null) { | ||||||
|  |             monsterData.observe(getViewLifecycleOwner(), monsters -> mAdapter.submitList(monsters)); | ||||||
|  |         } | ||||||
|  |         recyclerView.setAdapter(mAdapter); | ||||||
|  |         DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation()); | ||||||
|  |         recyclerView.addItemDecoration(dividerItemDecoration); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void navigateToMonsterDetail(Monster monster) { | ||||||
|  |         if (monster == null) { | ||||||
|  |             NavDirections action = SearchFragmentDirections.actionNavigationSearchToNavigationMonster(monster.id.toString()); | ||||||
|  |             Navigation.findNavController(requireView()).navigate(action); | ||||||
|  |         } else { | ||||||
|  |             Logger.logError("Can't navigate to MonsterDetail without a monster."); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static class ViewHolder { | ||||||
|  |         final RecyclerView monsterList; | ||||||
|  |         final EditText filterQuery; | ||||||
|  |  | ||||||
|  |         public ViewHolder(FragmentSearchBinding binding) { | ||||||
|  |             monsterList = binding.monsterList; | ||||||
|  |             filterQuery = binding.searchQuery; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,90 +1,52 @@ | |||||||
| package com.majinnaibu.monstercards.ui.search; | package com.majinnaibu.monstercards.ui.search; | ||||||
|  |  | ||||||
| import android.view.LayoutInflater; | import android.view.LayoutInflater; | ||||||
| import android.view.View; |  | ||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
| import android.widget.TextView; |  | ||||||
|  |  | ||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
| import androidx.recyclerview.widget.RecyclerView; | import androidx.recyclerview.widget.DiffUtil; | ||||||
|  | import androidx.recyclerview.widget.ListAdapter; | ||||||
|  |  | ||||||
| import com.majinnaibu.monstercards.R; | import com.majinnaibu.monstercards.databinding.SimpleListItemBinding; | ||||||
| import com.majinnaibu.monstercards.data.MonsterRepository; |  | ||||||
| import com.majinnaibu.monstercards.models.Monster; | import com.majinnaibu.monstercards.models.Monster; | ||||||
| import com.majinnaibu.monstercards.utils.Logger; | import com.majinnaibu.monstercards.ui.shared.SimpleListItemViewHolder; | ||||||
|  | import com.majinnaibu.monstercards.utils.ItemCallback; | ||||||
|  |  | ||||||
| import java.util.ArrayList; | public class SearchResultsRecyclerViewAdapter extends ListAdapter<Monster, SimpleListItemViewHolder<Monster>> { | ||||||
| import java.util.List; |     private static final DiffUtil.ItemCallback<Monster> DIFF_CALLBACK = new DiffUtil.ItemCallback<Monster>() { | ||||||
|  |         @Override | ||||||
| import io.reactivex.rxjava3.core.Flowable; |         public boolean areItemsTheSame(@NonNull Monster oldItem, @NonNull Monster newItem) { | ||||||
| import io.reactivex.rxjava3.disposables.Disposable; |             return Monster.areItemsTheSame(oldItem, newItem); | ||||||
|  |  | ||||||
| public class SearchResultsRecyclerViewAdapter extends RecyclerView.Adapter<SearchResultsRecyclerViewAdapter.ViewHolder> { |  | ||||||
|     private final MonsterRepository mRepository; |  | ||||||
|     private final ItemCallback mOnClickHandler; |  | ||||||
|     private String mSearchText; |  | ||||||
|     private List<Monster> mValues; |  | ||||||
|     private Disposable mSubscriptionHandler; |  | ||||||
|  |  | ||||||
|     public SearchResultsRecyclerViewAdapter(MonsterRepository repository, |  | ||||||
|                                             ItemCallback onClick) { |  | ||||||
|         mRepository = repository; |  | ||||||
|         mSearchText = ""; |  | ||||||
|         mValues = new ArrayList<>(); |  | ||||||
|         mOnClickHandler = onClick; |  | ||||||
|         mSubscriptionHandler = null; |  | ||||||
|  |  | ||||||
|         doSearch(mSearchText); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void doSearch(String searchText) { |  | ||||||
|         if (mSubscriptionHandler != null && !mSubscriptionHandler.isDisposed()) { |  | ||||||
|             mSubscriptionHandler.dispose(); |  | ||||||
|         } |         } | ||||||
|         mSearchText = searchText; |  | ||||||
|         Flowable<List<Monster>> foundMonsters = mRepository.searchMonsters(mSearchText); |         @Override | ||||||
|         mSubscriptionHandler = foundMonsters.subscribe(monsters -> { |         public boolean areContentsTheSame(@NonNull Monster oldItem, @NonNull Monster newItem) { | ||||||
|                     mValues = monsters; |             return Monster.areContentsTheSame(oldItem, newItem); | ||||||
|                     notifyDataSetChanged(); |         } | ||||||
|                 }, |     }; | ||||||
|                 throwable -> Logger.logError("Error performing search", throwable)); |     private final ItemCallback<Monster> mOnClick; | ||||||
|  |  | ||||||
|  |     public SearchResultsRecyclerViewAdapter(ItemCallback<Monster> onClick) { | ||||||
|  |         super(DIFF_CALLBACK); | ||||||
|  |         mOnClick = onClick; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @NonNull |     @NonNull | ||||||
|     @Override |     @Override | ||||||
|     public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { |     public SimpleListItemViewHolder<Monster> onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | ||||||
|         View view = LayoutInflater.from(parent.getContext()) |         SimpleListItemBinding binding = SimpleListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); | ||||||
|                 .inflate(R.layout.monster_list_content, parent, false); |         return new SimpleListItemViewHolder<>(binding); | ||||||
|         return new ViewHolder(view); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onBindViewHolder(@NonNull final ViewHolder holder, int position) { |     public void onBindViewHolder(@NonNull final SimpleListItemViewHolder<Monster> holder, int position) { | ||||||
|         Monster monster = mValues.get(position); |         Monster monster = getItem(position); | ||||||
|         holder.mContentView.setText(monster.name); |         holder.item = monster; | ||||||
|         holder.itemView.setTag(monster); |         holder.contentView.setText(monster.name); | ||||||
|         holder.itemView.setOnClickListener(view -> { |         holder.itemView.setOnClickListener(view -> { | ||||||
|             if (mOnClickHandler != null) { |             if (mOnClick != null) { | ||||||
|                 mOnClickHandler.onItem(monster); |                 mOnClick.onItem(holder.item); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public int getItemCount() { |  | ||||||
|         return mValues.size(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public interface ItemCallback { |  | ||||||
|         void onItem(Monster monster); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public static class ViewHolder extends RecyclerView.ViewHolder { |  | ||||||
|         final TextView mContentView; |  | ||||||
|  |  | ||||||
|         ViewHolder(View view) { |  | ||||||
|             super(view); |  | ||||||
|             mContentView = view.findViewById(R.id.content); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,118 @@ | |||||||
|  | package com.majinnaibu.monstercards.ui.search; | ||||||
|  |  | ||||||
|  | import android.app.Application; | ||||||
|  |  | ||||||
|  | import androidx.lifecycle.AndroidViewModel; | ||||||
|  | import androidx.lifecycle.LiveData; | ||||||
|  | import androidx.lifecycle.MediatorLiveData; | ||||||
|  | import androidx.lifecycle.MutableLiveData; | ||||||
|  |  | ||||||
|  | import com.majinnaibu.monstercards.AppDatabase; | ||||||
|  | import com.majinnaibu.monstercards.helpers.StringHelper; | ||||||
|  | import com.majinnaibu.monstercards.models.Monster; | ||||||
|  | import com.majinnaibu.monstercards.utils.Logger; | ||||||
|  |  | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Locale; | ||||||
|  |  | ||||||
|  | import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; | ||||||
|  | import io.reactivex.rxjava3.schedulers.Schedulers; | ||||||
|  | import io.reactivex.rxjava3.subscribers.DisposableSubscriber; | ||||||
|  |  | ||||||
|  | public class SearchViewModel extends AndroidViewModel { | ||||||
|  |     private final MutableLiveData<List<Monster>> mAllMonsters; | ||||||
|  |     private final MediatorLiveData<List<Monster>> mFilteredMonsters; | ||||||
|  |     private final MutableLiveData<String> mFilterText; | ||||||
|  |     private final AppDatabase mDB; | ||||||
|  |  | ||||||
|  |     public SearchViewModel(Application application) { | ||||||
|  |         super(application); | ||||||
|  |         mDB = AppDatabase.getInstance(application); | ||||||
|  |         mAllMonsters = new MutableLiveData<>(new ArrayList<>()); | ||||||
|  |         mFilterText = new MutableLiveData<>(""); | ||||||
|  |         mFilteredMonsters = new MediatorLiveData<>(); | ||||||
|  |         mFilteredMonsters.addSource( | ||||||
|  |                 mAllMonsters, | ||||||
|  |                 allMonsters -> mFilteredMonsters.setValue( | ||||||
|  |                         filterMonsters(allMonsters, mFilterText.getValue()))); | ||||||
|  |         mFilteredMonsters.addSource( | ||||||
|  |                 mFilterText, | ||||||
|  |                 filterText -> mFilteredMonsters.setValue( | ||||||
|  |                         filterMonsters(mAllMonsters.getValue(), filterText))); | ||||||
|  |         mDB.monsterDAO() | ||||||
|  |                 .getAll() | ||||||
|  |                 .observeOn(AndroidSchedulers.mainThread()) | ||||||
|  |                 .subscribeOn(Schedulers.io()) | ||||||
|  |                 .subscribe(new DisposableSubscriber<List<Monster>>() { | ||||||
|  |                     @Override | ||||||
|  |                     public void onNext(List<Monster> monsters) { | ||||||
|  |                         mAllMonsters.setValue(monsters); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     @Override | ||||||
|  |                     public void onError(Throwable t) { | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     @Override | ||||||
|  |                     public void onComplete() { | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private boolean monsterMatchesFilter(Monster monster, String filterText) { | ||||||
|  |         if (StringHelper.isNullOrEmpty(filterText)) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (StringHelper.containsCaseInsensitive(monster.name, filterText)) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (StringHelper.containsCaseInsensitive(monster.size, filterText)) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (StringHelper.containsCaseInsensitive(monster.type, filterText)) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (StringHelper.containsCaseInsensitive(monster.subtype, filterText)) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (StringHelper.containsCaseInsensitive(monster.alignment, filterText)) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private List<Monster> filterMonsters(List<Monster> allMonsters, String filterText) { | ||||||
|  |         ArrayList<Monster> filteredMonsters = new ArrayList<>(); | ||||||
|  |         filterText = filterText.toLowerCase(Locale.ROOT); | ||||||
|  |         if (allMonsters != null) { | ||||||
|  |             for (Monster monster : allMonsters) { | ||||||
|  |                 // TODO: do the filtering like the iOS app does. | ||||||
|  |                 Logger.logUnimplementedFeature("do the filtering like the iOS app does"); | ||||||
|  |                 // TODO: consider splitting search text into words and if each word appears in any of these fields return true e.g, "large demon" would match large in size and demon in type. | ||||||
|  |                 // TODO: add tags and search by tags | ||||||
|  |                 // TODO: add a display of what fields matched on each item in the results | ||||||
|  |                 // TODO: make the criteria configurable from this screen | ||||||
|  |                 // TODO: find a way to add challenge rating as a search criteria | ||||||
|  |                 if (monsterMatchesFilter(monster, filterText)) { | ||||||
|  |                     filteredMonsters.add(monster); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return filteredMonsters; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public LiveData<List<Monster>> getMatchedMonsters() { | ||||||
|  |         return mFilteredMonsters; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setFilterText(String filterText) { | ||||||
|  |         mFilterText.setValue(filterText); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,17 @@ | |||||||
|  | package com.majinnaibu.monstercards.ui.shared; | ||||||
|  |  | ||||||
|  | import android.widget.TextView; | ||||||
|  |  | ||||||
|  | import androidx.recyclerview.widget.RecyclerView; | ||||||
|  |  | ||||||
|  | import com.majinnaibu.monstercards.databinding.SimpleListItemBinding; | ||||||
|  |  | ||||||
|  | public class SimpleListItemViewHolder<T> extends RecyclerView.ViewHolder { | ||||||
|  |     public final TextView contentView; | ||||||
|  |     public T item; | ||||||
|  |  | ||||||
|  |     public SimpleListItemViewHolder(SimpleListItemBinding binding) { | ||||||
|  |         super(binding.getRoot()); | ||||||
|  |         contentView = binding.content; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,5 @@ | |||||||
|  | package com.majinnaibu.monstercards.utils; | ||||||
|  |  | ||||||
|  | public interface ItemCallback<T> { | ||||||
|  |     void onItem(T item); | ||||||
|  | } | ||||||
| @@ -31,5 +31,4 @@ | |||||||
|         app:layout_constraintTop_toTopOf="parent" |         app:layout_constraintTop_toTopOf="parent" | ||||||
|         app:layout_constraintVertical_bias="0.0" |         app:layout_constraintVertical_bias="0.0" | ||||||
|         app:navGraph="@navigation/mobile_navigation" /> |         app:navGraph="@navigation/mobile_navigation" /> | ||||||
|  |  | ||||||
| </androidx.constraintlayout.widget.ConstraintLayout> | </androidx.constraintlayout.widget.ConstraintLayout> | ||||||
| @@ -55,5 +55,4 @@ | |||||||
|         app:layout_constraintStart_toStartOf="parent" |         app:layout_constraintStart_toStartOf="parent" | ||||||
|         app:layout_constraintTop_toTopOf="parent" |         app:layout_constraintTop_toTopOf="parent" | ||||||
|         tools:text="+5" /> |         tools:text="+5" /> | ||||||
|  |  | ||||||
| </androidx.constraintlayout.widget.ConstraintLayout> | </androidx.constraintlayout.widget.ConstraintLayout> | ||||||
|   | |||||||
| @@ -35,6 +35,4 @@ | |||||||
|         app:layout_constraintStart_toStartOf="parent" |         app:layout_constraintStart_toStartOf="parent" | ||||||
|         app:layout_constraintTop_toBottomOf="@id/name" |         app:layout_constraintTop_toBottomOf="@id/name" | ||||||
|         tools:text="Melee Weapon Attack: +8 to hit, reach 10 ft., one target. Hit: 14 (2d8 + 5) bludgeoning damage." /> |         tools:text="Melee Weapon Attack: +8 to hit, reach 10 ft., one target. Hit: 14 (2d8 + 5) bludgeoning damage." /> | ||||||
|  |  | ||||||
|  |  | ||||||
| </androidx.constraintlayout.widget.ConstraintLayout> | </androidx.constraintlayout.widget.ConstraintLayout> | ||||||
|   | |||||||
| @@ -31,5 +31,4 @@ | |||||||
|         app:layout_constraintStart_toStartOf="parent" |         app:layout_constraintStart_toStartOf="parent" | ||||||
|         app:layout_constraintTop_toTopOf="parent" |         app:layout_constraintTop_toTopOf="parent" | ||||||
|         tools:text="17" /> |         tools:text="17" /> | ||||||
|  |  | ||||||
| </androidx.constraintlayout.widget.ConstraintLayout> | </androidx.constraintlayout.widget.ConstraintLayout> | ||||||
|   | |||||||
| @@ -31,5 +31,4 @@ | |||||||
|         app:layout_constraintStart_toStartOf="parent" |         app:layout_constraintStart_toStartOf="parent" | ||||||
|         app:layout_constraintTop_toTopOf="parent" |         app:layout_constraintTop_toTopOf="parent" | ||||||
|         tools:text="1/8" /> |         tools:text="1/8" /> | ||||||
|  |  | ||||||
| </androidx.constraintlayout.widget.ConstraintLayout> | </androidx.constraintlayout.widget.ConstraintLayout> | ||||||
|   | |||||||
| @@ -31,5 +31,4 @@ | |||||||
|         app:layout_constraintStart_toStartOf="parent" |         app:layout_constraintStart_toStartOf="parent" | ||||||
|         app:layout_constraintTop_toTopOf="parent" |         app:layout_constraintTop_toTopOf="parent" | ||||||
|         tools:text="367" /> |         tools:text="367" /> | ||||||
|  |  | ||||||
| </androidx.constraintlayout.widget.ConstraintLayout> | </androidx.constraintlayout.widget.ConstraintLayout> | ||||||
|   | |||||||
| @@ -31,5 +31,4 @@ | |||||||
|         app:layout_constraintStart_toStartOf="parent" |         app:layout_constraintStart_toStartOf="parent" | ||||||
|         app:layout_constraintTop_toTopOf="parent" |         app:layout_constraintTop_toTopOf="parent" | ||||||
|         tools:text="+2" /> |         tools:text="+2" /> | ||||||
|  |  | ||||||
| </androidx.constraintlayout.widget.ConstraintLayout> | </androidx.constraintlayout.widget.ConstraintLayout> | ||||||
|   | |||||||
| @@ -169,6 +169,4 @@ | |||||||
|             android:layout_marginHorizontal="@dimen/padding_small" |             android:layout_marginHorizontal="@dimen/padding_small" | ||||||
|             android:layout_weight="1" /> |             android:layout_weight="1" /> | ||||||
|     </LinearLayout> |     </LinearLayout> | ||||||
|  |  | ||||||
|  |  | ||||||
| </androidx.constraintlayout.widget.ConstraintLayout> | </androidx.constraintlayout.widget.ConstraintLayout> | ||||||
|   | |||||||
| @@ -1,78 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" |  | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="wrap_content" |  | ||||||
|     android:layout_margin="@dimen/text_margin" |  | ||||||
|     tools:context=".ui.components.AdvantagePicker"> |  | ||||||
|     <!-- // TODO: style this control to look less awful by default --> |  | ||||||
|  |  | ||||||
|     <TextView |  | ||||||
|         android:id="@+id/label" |  | ||||||
|         android:layout_width="wrap_content" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:layout_marginHorizontal="@dimen/padding_normal" |  | ||||||
|         android:layout_marginTop="@dimen/padding_small" |  | ||||||
|         android:text="@string/label_advantage" |  | ||||||
|         android:textAppearance="@android:style/TextAppearance.Material.Body1" |  | ||||||
|         app:layout_constraintStart_toStartOf="parent" |  | ||||||
|         app:layout_constraintTop_toTopOf="parent" /> |  | ||||||
|  |  | ||||||
|     <RadioGroup |  | ||||||
|         android:id="@+id/group" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:orientation="horizontal" |  | ||||||
|         app:layout_constraintBottom_toBottomOf="parent" |  | ||||||
|         app:layout_constraintTop_toBottomOf="@id/label"> |  | ||||||
|  |  | ||||||
|         <com.google.android.material.radiobutton.MaterialRadioButton |  | ||||||
|             android:id="@+id/hasNoAdvantage" |  | ||||||
|             android:layout_width="0dp" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             android:layout_marginHorizontal="4dp" |  | ||||||
|             android:layout_marginVertical="8dp" |  | ||||||
|             android:layout_weight="1" |  | ||||||
|             android:background="@drawable/radio_button_selector" |  | ||||||
|             android:button="@android:color/transparent" |  | ||||||
|             android:gravity="center" |  | ||||||
|             android:padding="8dp" |  | ||||||
|             android:text="@string/label_advantage_none" |  | ||||||
|             android:textAppearance="@android:style/TextAppearance.Material.Button" |  | ||||||
|             android:textColor="@color/radio_button_text" |  | ||||||
|             tools:checked="true" /> |  | ||||||
|  |  | ||||||
|         <com.google.android.material.radiobutton.MaterialRadioButton |  | ||||||
|             android:id="@+id/hasAdvantage" |  | ||||||
|             android:layout_width="0dp" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             android:layout_marginHorizontal="4dp" |  | ||||||
|             android:layout_marginVertical="8dp" |  | ||||||
|             android:layout_weight="1" |  | ||||||
|             android:background="@drawable/radio_button_selector" |  | ||||||
|             android:button="@android:color/transparent" |  | ||||||
|             android:gravity="center" |  | ||||||
|             android:padding="8dp" |  | ||||||
|             android:text="@string/label_advantage_advantage" |  | ||||||
|             android:textAppearance="@android:style/TextAppearance.Material.Button" |  | ||||||
|             android:textColor="@color/radio_button_text" |  | ||||||
|             tools:checked="false" /> |  | ||||||
|  |  | ||||||
|         <com.google.android.material.radiobutton.MaterialRadioButton |  | ||||||
|             android:id="@+id/hasDisadvantage" |  | ||||||
|             android:layout_width="0dp" |  | ||||||
|             android:layout_height="wrap_content" |  | ||||||
|             android:layout_marginHorizontal="4dp" |  | ||||||
|             android:layout_marginVertical="8dp" |  | ||||||
|             android:layout_weight="1" |  | ||||||
|             android:background="@drawable/radio_button_selector" |  | ||||||
|             android:button="@android:color/transparent" |  | ||||||
|             android:gravity="center" |  | ||||||
|             android:padding="8dp" |  | ||||||
|             android:text="@string/label_advantage_disadvantage" |  | ||||||
|             android:textAppearance="@android:style/TextAppearance.Material.Button" |  | ||||||
|             android:textColor="@color/radio_button_text" |  | ||||||
|             tools:checked="false" /> |  | ||||||
|     </RadioGroup> |  | ||||||
| </androidx.constraintlayout.widget.ConstraintLayout> |  | ||||||
| @@ -1,42 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="wrap_content" |  | ||||||
|     android:orientation="horizontal"> |  | ||||||
|  |  | ||||||
|     <TextView |  | ||||||
|         android:id="@+id/content" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:layout_margin="@dimen/text_margin" |  | ||||||
|         android:textAppearance="?attr/textAppearanceListItem" /> |  | ||||||
|  |  | ||||||
|     <!--            <include--> |  | ||||||
|     <!--                layout="@layout/card_monster"--> |  | ||||||
|     <!--                android:layout_width="0dp"--> |  | ||||||
|     <!--                android:layout_height="wrap_content"--> |  | ||||||
|     <!--                android:layout_columnWeight="1"--> |  | ||||||
|     <!--                android:layout_marginVertical="8dp"/>--> |  | ||||||
|  |  | ||||||
|     <!--            <include--> |  | ||||||
|     <!--                layout="@layout/card_monster_short"--> |  | ||||||
|     <!--                android:layout_width="0dp"--> |  | ||||||
|     <!--                android:layout_height="wrap_content"--> |  | ||||||
|     <!--                android:layout_columnWeight="1"--> |  | ||||||
|     <!--                android:layout_marginVertical="8dp"/>--> |  | ||||||
|  |  | ||||||
|     <!--            <include--> |  | ||||||
|     <!--                layout="@layout/tile_monster"--> |  | ||||||
|     <!--                android:layout_width="0dp"--> |  | ||||||
|     <!--                android:layout_height="wrap_content"--> |  | ||||||
|     <!--                android:layout_columnWeight="1"--> |  | ||||||
|     <!--                android:layout_marginVertical="8dp"/>--> |  | ||||||
|  |  | ||||||
|     <!--            <include--> |  | ||||||
|     <!--                layout="@layout/tile_monster_short"--> |  | ||||||
|     <!--                android:layout_width="0dp"--> |  | ||||||
|     <!--                android:layout_height="wrap_content"--> |  | ||||||
|     <!--                android:layout_columnWeight="1"--> |  | ||||||
|     <!--                android:layout_marginVertical="8dp" />--> |  | ||||||
|  |  | ||||||
| </LinearLayout> |  | ||||||
| @@ -17,7 +17,7 @@ | |||||||
|         app:layout_constraintEnd_toEndOf="parent" |         app:layout_constraintEnd_toEndOf="parent" | ||||||
|         app:layout_constraintStart_toStartOf="parent" |         app:layout_constraintStart_toStartOf="parent" | ||||||
|         app:layout_constraintTop_toTopOf="parent" |         app:layout_constraintTop_toTopOf="parent" | ||||||
|         tools:listitem="@layout/fragment_edit_languages_list_item" /> |         tools:listitem="@layout/simple_list_item" /> | ||||||
|  |  | ||||||
|     <com.google.android.material.floatingactionbutton.FloatingActionButton |     <com.google.android.material.floatingactionbutton.FloatingActionButton | ||||||
|         android:id="@+id/add_language" |         android:id="@+id/add_language" | ||||||
|   | |||||||
| @@ -184,6 +184,5 @@ | |||||||
|             android:text="@string/label_regional_effects" |             android:text="@string/label_regional_effects" | ||||||
|             android:textSize="@dimen/text_h4_size" |             android:textSize="@dimen/text_h4_size" | ||||||
|             app:drawableEndCompat="@drawable/ic_chevron_right_24" /> |             app:drawableEndCompat="@drawable/ic_chevron_right_24" /> | ||||||
|  |  | ||||||
|     </LinearLayout> |     </LinearLayout> | ||||||
| </ScrollView> | </ScrollView> | ||||||
|   | |||||||
| @@ -167,5 +167,4 @@ | |||||||
|                 android:layout_height="wrap_content" /> |                 android:layout_height="wrap_content" /> | ||||||
|         </LinearLayout> |         </LinearLayout> | ||||||
|     </LinearLayout> |     </LinearLayout> | ||||||
|  |  | ||||||
| </ScrollView> | </ScrollView> | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ | |||||||
|         app:layout_constraintEnd_toEndOf="parent" |         app:layout_constraintEnd_toEndOf="parent" | ||||||
|         app:layout_constraintStart_toStartOf="parent" |         app:layout_constraintStart_toStartOf="parent" | ||||||
|         app:layout_constraintTop_toTopOf="parent" |         app:layout_constraintTop_toTopOf="parent" | ||||||
|         tools:listitem="@layout/fragment_edit_skills_list_item" /> |         tools:listitem="@layout/simple_list_item" /> | ||||||
|  |  | ||||||
|     <com.google.android.material.floatingactionbutton.FloatingActionButton |     <com.google.android.material.floatingactionbutton.FloatingActionButton | ||||||
|         android:id="@+id/add_skill" |         android:id="@+id/add_skill" | ||||||
|   | |||||||
| @@ -1,13 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="wrap_content" |  | ||||||
|     android:orientation="horizontal"> |  | ||||||
|  |  | ||||||
|     <TextView |  | ||||||
|         android:id="@+id/content" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:layout_margin="@dimen/text_margin" |  | ||||||
|         android:textAppearance="?attr/textAppearanceListItem" /> |  | ||||||
| </LinearLayout> |  | ||||||
| @@ -17,7 +17,7 @@ | |||||||
|         app:layout_constraintEnd_toEndOf="parent" |         app:layout_constraintEnd_toEndOf="parent" | ||||||
|         app:layout_constraintStart_toStartOf="parent" |         app:layout_constraintStart_toStartOf="parent" | ||||||
|         app:layout_constraintTop_toTopOf="parent" |         app:layout_constraintTop_toTopOf="parent" | ||||||
|         tools:listitem="@layout/fragment_edit_traits_list_item" /> |         tools:listitem="@layout/simple_list_item" /> | ||||||
|  |  | ||||||
|     <com.google.android.material.floatingactionbutton.FloatingActionButton |     <com.google.android.material.floatingactionbutton.FloatingActionButton | ||||||
|         android:id="@+id/add_item" |         android:id="@+id/add_item" | ||||||
|   | |||||||
| @@ -1,13 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="wrap_content" |  | ||||||
|     android:orientation="horizontal"> |  | ||||||
|  |  | ||||||
|     <TextView |  | ||||||
|         android:id="@+id/content" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:layout_margin="@dimen/text_margin" |  | ||||||
|         android:textAppearance="?attr/textAppearanceListItem" /> |  | ||||||
| </LinearLayout> |  | ||||||
| @@ -17,7 +17,7 @@ | |||||||
|         app:layout_constraintEnd_toEndOf="parent" |         app:layout_constraintEnd_toEndOf="parent" | ||||||
|         app:layout_constraintStart_toStartOf="parent" |         app:layout_constraintStart_toStartOf="parent" | ||||||
|         app:layout_constraintTop_toTopOf="parent" |         app:layout_constraintTop_toTopOf="parent" | ||||||
|         tools:listitem="@layout/fragment_edit_traits_list_item" /> |         tools:listitem="@layout/simple_list_item" /> | ||||||
|  |  | ||||||
|     <com.google.android.material.floatingactionbutton.FloatingActionButton |     <com.google.android.material.floatingactionbutton.FloatingActionButton | ||||||
|         android:id="@+id/add_trait" |         android:id="@+id/add_trait" | ||||||
|   | |||||||
| @@ -1,13 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="wrap_content" |  | ||||||
|     android:orientation="horizontal"> |  | ||||||
|  |  | ||||||
|     <TextView |  | ||||||
|         android:id="@+id/content" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:layout_margin="@dimen/text_margin" |  | ||||||
|         android:textAppearance="?attr/textAppearanceListItem" /> |  | ||||||
| </LinearLayout> |  | ||||||
| @@ -17,7 +17,7 @@ | |||||||
|         app:layout_constraintStart_toStartOf="parent" |         app:layout_constraintStart_toStartOf="parent" | ||||||
|         app:layout_constraintTop_toTopOf="parent" |         app:layout_constraintTop_toTopOf="parent" | ||||||
|         tools:context=".MonsterListFragment" |         tools:context=".MonsterListFragment" | ||||||
|         tools:listitem="@layout/monster_list_content" /> |         tools:listitem="@layout/simple_list_item" /> | ||||||
|  |  | ||||||
|     <com.google.android.material.floatingactionbutton.FloatingActionButton |     <com.google.android.material.floatingactionbutton.FloatingActionButton | ||||||
|         android:id="@+id/fab" |         android:id="@+id/fab" | ||||||
|   | |||||||
| @@ -34,7 +34,5 @@ | |||||||
|         app:layout_constraintStart_toStartOf="parent" |         app:layout_constraintStart_toStartOf="parent" | ||||||
|         app:layout_constraintTop_toBottomOf="@+id/search_query" |         app:layout_constraintTop_toBottomOf="@+id/search_query" | ||||||
|         tools:context=".SearchResultsFragment" |         tools:context=".SearchResultsFragment" | ||||||
|         tools:listitem="@layout/monster_list_content" /> |         tools:listitem="@layout/simple_list_item" /> | ||||||
|  |  | ||||||
|  |  | ||||||
| </androidx.constraintlayout.widget.ConstraintLayout> | </androidx.constraintlayout.widget.ConstraintLayout> | ||||||
|   | |||||||
| @@ -1,14 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="wrap_content" |  | ||||||
|     android:orientation="horizontal"> |  | ||||||
|     <!-- // TODO: combine all of these similar list layouts into a single one --> |  | ||||||
|  |  | ||||||
|     <TextView |  | ||||||
|         android:id="@+id/content" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:layout_margin="@dimen/text_margin" |  | ||||||
|         android:textAppearance="?attr/textAppearanceListItem" /> |  | ||||||
| </LinearLayout> |  | ||||||
| @@ -97,5 +97,4 @@ | |||||||
|         app:layout_constraintEnd_toEndOf="parent" |         app:layout_constraintEnd_toEndOf="parent" | ||||||
|         app:layout_constraintStart_toStartOf="parent" |         app:layout_constraintStart_toStartOf="parent" | ||||||
|         app:layout_constraintTop_toTopOf="parent" /> |         app:layout_constraintTop_toTopOf="parent" /> | ||||||
|  |  | ||||||
| </androidx.constraintlayout.widget.ConstraintLayout> | </androidx.constraintlayout.widget.ConstraintLayout> | ||||||
|   | |||||||
| @@ -84,7 +84,5 @@ | |||||||
|             layout="@layout/card_challenge_rating" |             layout="@layout/card_challenge_rating" | ||||||
|             android:layout_width="@dimen/icon_size" |             android:layout_width="@dimen/icon_size" | ||||||
|             android:layout_height="@dimen/icon_size" /> |             android:layout_height="@dimen/icon_size" /> | ||||||
|  |  | ||||||
|     </LinearLayout> |     </LinearLayout> | ||||||
|  |  | ||||||
| </androidx.constraintlayout.widget.ConstraintLayout> | </androidx.constraintlayout.widget.ConstraintLayout> | ||||||
|   | |||||||
| @@ -5,9 +5,8 @@ buildscript { | |||||||
|         mavenCentral() |         mavenCentral() | ||||||
|     } |     } | ||||||
|     dependencies { |     dependencies { | ||||||
|         classpath 'com.android.tools.build:gradle:7.0.4' |         classpath "com.android.tools.build:gradle:7.0.1" | ||||||
|         classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5" |         classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5" | ||||||
|         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10" |  | ||||||
|  |  | ||||||
|         // NOTE: Do not place your application dependencies here; they belong |         // NOTE: Do not place your application dependencies here; they belong | ||||||
|         // in the individual module build.gradle files |         // in the individual module build.gradle files | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user