diff --git a/app/build.gradle b/app/build.gradle index eb965a846..0e51f4861 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,8 +20,8 @@ android { applicationId "com.eventyay.attendee" minSdkVersion 21 targetSdkVersion 28 - versionCode 7 - versionName "0.2.0" + versionCode 8 + versionName "0.2.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true multiDexEnabled true @@ -109,7 +109,7 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha5' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha04' - implementation 'com.google.android.material:material:1.1.0-alpha05' + implementation 'com.google.android.material:material:1.1.0-alpha06' implementation "androidx.browser:browser:1.0.0" implementation 'androidx.exifinterface:exifinterface:1.0.0' implementation "androidx.lifecycle:lifecycle-extensions:${lifecycle_version}" diff --git a/app/schemas/org.fossasia.openevent.general.OpenEventDatabase/4.json b/app/schemas/org.fossasia.openevent.general.OpenEventDatabase/4.json new file mode 100644 index 000000000..b372d6aaa --- /dev/null +++ b/app/schemas/org.fossasia.openevent.general.OpenEventDatabase/4.json @@ -0,0 +1,1430 @@ +{ + "formatVersion": 1, + "database": { + "version": 4, + "identityHash": "f2497951a09fbddef232913f8b311f06", + "entities": [ + { + "tableName": "Event", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `identifier` TEXT NOT NULL, `startsAt` TEXT NOT NULL, `endsAt` TEXT NOT NULL, `timezone` TEXT NOT NULL, `privacy` TEXT NOT NULL, `paymentCountry` TEXT, `paypalEmail` TEXT, `thumbnailImageUrl` TEXT, `schedulePublishedOn` TEXT, `paymentCurrency` TEXT, `organizerDescription` TEXT, `originalImageUrl` TEXT, `onsiteDetails` TEXT, `organizerName` TEXT, `largeImageUrl` TEXT, `deletedAt` TEXT, `ticketUrl` TEXT, `locationName` TEXT, `codeOfConduct` TEXT, `state` TEXT, `searchableLocationName` TEXT, `description` TEXT, `pentabarfUrl` TEXT, `xcalUrl` TEXT, `logoUrl` TEXT, `externalEventUrl` TEXT, `iconImageUrl` TEXT, `icalUrl` TEXT, `createdAt` TEXT, `bankDetails` TEXT, `chequeDetails` TEXT, `isComplete` INTEGER NOT NULL, `latitude` REAL, `longitude` REAL, `refundPolicy` TEXT, `canPayByStripe` INTEGER NOT NULL, `canPayByCheque` INTEGER NOT NULL, `canPayByBank` INTEGER NOT NULL, `canPayByPaypal` INTEGER NOT NULL, `canPayOnsite` INTEGER NOT NULL, `isSponsorsEnabled` INTEGER NOT NULL, `hasOrganizerInfo` INTEGER NOT NULL, `isSessionsSpeakersEnabled` INTEGER NOT NULL, `isTicketingEnabled` INTEGER NOT NULL, `isTaxEnabled` INTEGER NOT NULL, `isMapShown` INTEGER NOT NULL, `favorite` INTEGER NOT NULL, `eventTopic` TEXT, `eventType` TEXT, `eventSubTopic` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "identifier", + "columnName": "identifier", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "startsAt", + "columnName": "startsAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "endsAt", + "columnName": "endsAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timezone", + "columnName": "timezone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privacy", + "columnName": "privacy", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentCountry", + "columnName": "paymentCountry", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "paypalEmail", + "columnName": "paypalEmail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnailImageUrl", + "columnName": "thumbnailImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "schedulePublishedOn", + "columnName": "schedulePublishedOn", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "paymentCurrency", + "columnName": "paymentCurrency", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "organizerDescription", + "columnName": "organizerDescription", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "originalImageUrl", + "columnName": "originalImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "onsiteDetails", + "columnName": "onsiteDetails", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "organizerName", + "columnName": "organizerName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "largeImageUrl", + "columnName": "largeImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "deletedAt", + "columnName": "deletedAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ticketUrl", + "columnName": "ticketUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "locationName", + "columnName": "locationName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "codeOfConduct", + "columnName": "codeOfConduct", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "searchableLocationName", + "columnName": "searchableLocationName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pentabarfUrl", + "columnName": "pentabarfUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "xcalUrl", + "columnName": "xcalUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "logoUrl", + "columnName": "logoUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "externalEventUrl", + "columnName": "externalEventUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconImageUrl", + "columnName": "iconImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "icalUrl", + "columnName": "icalUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "bankDetails", + "columnName": "bankDetails", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "chequeDetails", + "columnName": "chequeDetails", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isComplete", + "columnName": "isComplete", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latitude", + "columnName": "latitude", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "longitude", + "columnName": "longitude", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "refundPolicy", + "columnName": "refundPolicy", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "canPayByStripe", + "columnName": "canPayByStripe", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canPayByCheque", + "columnName": "canPayByCheque", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canPayByBank", + "columnName": "canPayByBank", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canPayByPaypal", + "columnName": "canPayByPaypal", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canPayOnsite", + "columnName": "canPayOnsite", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isSponsorsEnabled", + "columnName": "isSponsorsEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasOrganizerInfo", + "columnName": "hasOrganizerInfo", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isSessionsSpeakersEnabled", + "columnName": "isSessionsSpeakersEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isTicketingEnabled", + "columnName": "isTicketingEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isTaxEnabled", + "columnName": "isTaxEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isMapShown", + "columnName": "isMapShown", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "eventTopic", + "columnName": "eventTopic", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "eventType", + "columnName": "eventType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "eventSubTopic", + "columnName": "eventSubTopic", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_Event_eventTopic", + "unique": false, + "columnNames": [ + "eventTopic" + ], + "createSql": "CREATE INDEX `index_Event_eventTopic` ON `${TABLE_NAME}` (`eventTopic`)" + }, + { + "name": "index_Event_eventType", + "unique": false, + "columnNames": [ + "eventType" + ], + "createSql": "CREATE INDEX `index_Event_eventType` ON `${TABLE_NAME}` (`eventType`)" + }, + { + "name": "index_Event_eventSubTopic", + "unique": false, + "columnNames": [ + "eventSubTopic" + ], + "createSql": "CREATE INDEX `index_Event_eventSubTopic` ON `${TABLE_NAME}` (`eventSubTopic`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "User", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `firstName` TEXT, `lastName` TEXT, `email` TEXT, `contact` TEXT, `details` TEXT, `thumbnailImageUrl` TEXT, `iconImageUrl` TEXT, `smallImageUrl` TEXT, `avatarUrl` TEXT, `facebookUrl` TEXT, `twitterUrl` TEXT, `instagramUrl` TEXT, `googlePlusUrl` TEXT, `originalImageUrl` TEXT, `isVerified` INTEGER NOT NULL, `isAdmin` INTEGER, `isSuperAdmin` INTEGER, `createdAt` TEXT, `lastAccessedAt` TEXT, `deletedAt` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "firstName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastName", + "columnName": "lastName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "details", + "columnName": "details", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnailImageUrl", + "columnName": "thumbnailImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconImageUrl", + "columnName": "iconImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "smallImageUrl", + "columnName": "smallImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "avatarUrl", + "columnName": "avatarUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "facebookUrl", + "columnName": "facebookUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "twitterUrl", + "columnName": "twitterUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "instagramUrl", + "columnName": "instagramUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "googlePlusUrl", + "columnName": "googlePlusUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "originalImageUrl", + "columnName": "originalImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isVerified", + "columnName": "isVerified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isAdmin", + "columnName": "isAdmin", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isSuperAdmin", + "columnName": "isSuperAdmin", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastAccessedAt", + "columnName": "lastAccessedAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "deletedAt", + "columnName": "deletedAt", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SocialLink", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `link` TEXT NOT NULL, `name` TEXT NOT NULL, `event` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`event`) REFERENCES `Event`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "event", + "columnName": "event", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_SocialLink_event", + "unique": false, + "columnNames": [ + "event" + ], + "createSql": "CREATE INDEX `index_SocialLink_event` ON `${TABLE_NAME}` (`event`)" + } + ], + "foreignKeys": [ + { + "table": "Event", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "event" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "Ticket", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `description` TEXT, `type` TEXT, `name` TEXT NOT NULL, `maxOrder` INTEGER NOT NULL, `isFeeAbsorbed` INTEGER, `isDescriptionVisible` INTEGER, `price` REAL, `position` TEXT, `quantity` TEXT, `isHidden` INTEGER, `salesStartsAt` TEXT, `salesEndsAt` TEXT, `minOrder` INTEGER NOT NULL, `event` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`event`) REFERENCES `Event`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "maxOrder", + "columnName": "maxOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFeeAbsorbed", + "columnName": "isFeeAbsorbed", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isDescriptionVisible", + "columnName": "isDescriptionVisible", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "price", + "columnName": "price", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "quantity", + "columnName": "quantity", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isHidden", + "columnName": "isHidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "salesStartsAt", + "columnName": "salesStartsAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "salesEndsAt", + "columnName": "salesEndsAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "minOrder", + "columnName": "minOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "event", + "columnName": "event", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_Ticket_event", + "unique": false, + "columnNames": [ + "event" + ], + "createSql": "CREATE INDEX `index_Ticket_event` ON `${TABLE_NAME}` (`event`)" + } + ], + "foreignKeys": [ + { + "table": "Event", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "event" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "Attendee", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `firstname` TEXT, `lastname` TEXT, `email` TEXT, `address` TEXT, `city` TEXT, `state` TEXT, `country` TEXT, `isCheckedIn` INTEGER, `pdfUrl` TEXT, `ticketId` TEXT, `event` INTEGER, `ticket` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`event`) REFERENCES `Event`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`ticket`) REFERENCES `Ticket`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firstname", + "columnName": "firstname", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastname", + "columnName": "lastname", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "city", + "columnName": "city", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "country", + "columnName": "country", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isCheckedIn", + "columnName": "isCheckedIn", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pdfUrl", + "columnName": "pdfUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ticketId", + "columnName": "ticketId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "event", + "columnName": "event", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ticket", + "columnName": "ticket", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_Attendee_event", + "unique": false, + "columnNames": [ + "event" + ], + "createSql": "CREATE INDEX `index_Attendee_event` ON `${TABLE_NAME}` (`event`)" + } + ], + "foreignKeys": [ + { + "table": "Event", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "event" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "Ticket", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "ticket" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "EventTopic", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `slug` TEXT NOT NULL, `event` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`event`) REFERENCES `Event`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "slug", + "columnName": "slug", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "event", + "columnName": "event", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_EventTopic_event", + "unique": false, + "columnNames": [ + "event" + ], + "createSql": "CREATE INDEX `index_EventTopic_event` ON `${TABLE_NAME}` (`event`)" + } + ], + "foreignKeys": [ + { + "table": "Event", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "event" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "Order", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `paymentMode` TEXT, `country` TEXT, `status` TEXT, `amount` REAL, `identifier` TEXT, `orderNotes` TEXT, `completedAt` TEXT, `city` TEXT, `address` TEXT, `createdAt` TEXT, `zipcode` TEXT, `paidVia` TEXT, `discountCodeId` TEXT, `ticketsPdfUrl` TEXT, `transactionId` TEXT, `event` INTEGER, `attendees` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`event`) REFERENCES `Event`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`attendees`) REFERENCES `Attendee`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "paymentMode", + "columnName": "paymentMode", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "country", + "columnName": "country", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "identifier", + "columnName": "identifier", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "orderNotes", + "columnName": "orderNotes", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "completedAt", + "columnName": "completedAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "city", + "columnName": "city", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "zipcode", + "columnName": "zipcode", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "paidVia", + "columnName": "paidVia", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "discountCodeId", + "columnName": "discountCodeId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ticketsPdfUrl", + "columnName": "ticketsPdfUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "transactionId", + "columnName": "transactionId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "event", + "columnName": "event", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "attendees", + "columnName": "attendees", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_Order_event", + "unique": false, + "columnNames": [ + "event" + ], + "createSql": "CREATE INDEX `index_Order_event` ON `${TABLE_NAME}` (`event`)" + } + ], + "foreignKeys": [ + { + "table": "Event", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "event" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "Attendee", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "attendees" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "CustomForm", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `form` TEXT NOT NULL, `fieldIdentifier` TEXT NOT NULL, `type` TEXT NOT NULL, `isRequired` INTEGER, `isIncluded` INTEGER, `isFixed` INTEGER, `ticketsNumber` INTEGER, `event` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`event`) REFERENCES `Event`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "form", + "columnName": "form", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fieldIdentifier", + "columnName": "fieldIdentifier", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isRequired", + "columnName": "isRequired", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isIncluded", + "columnName": "isIncluded", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isFixed", + "columnName": "isFixed", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ticketsNumber", + "columnName": "ticketsNumber", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "event", + "columnName": "event", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [ + { + "table": "Event", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "event" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "Speaker", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, `email` TEXT, `photoUrl` TEXT, `shortBiography` TEXT, `longBiography` TEXT, `speakingExperience` TEXT, `position` TEXT, `mobile` TEXT, `location` TEXT, `country` TEXT, `city` TEXT, `organisation` TEXT, `gender` TEXT, `website` TEXT, `twitter` TEXT, `facebook` TEXT, `linkedin` TEXT, `github` TEXT, `isFeatured` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "photoUrl", + "columnName": "photoUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shortBiography", + "columnName": "shortBiography", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "longBiography", + "columnName": "longBiography", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "speakingExperience", + "columnName": "speakingExperience", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mobile", + "columnName": "mobile", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "country", + "columnName": "country", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "city", + "columnName": "city", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "organisation", + "columnName": "organisation", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "website", + "columnName": "website", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "twitter", + "columnName": "twitter", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "facebook", + "columnName": "facebook", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "linkedin", + "columnName": "linkedin", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "github", + "columnName": "github", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isFeatured", + "columnName": "isFeatured", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SpeakerWithEvent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`event_id` INTEGER NOT NULL, `speaker_id` INTEGER NOT NULL, PRIMARY KEY(`event_id`, `speaker_id`), FOREIGN KEY(`event_id`) REFERENCES `Event`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`speaker_id`) REFERENCES `Speaker`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "eventId", + "columnName": "event_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "speakerId", + "columnName": "speaker_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "event_id", + "speaker_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_SpeakerWithEvent_event_id", + "unique": false, + "columnNames": [ + "event_id" + ], + "createSql": "CREATE INDEX `index_SpeakerWithEvent_event_id` ON `${TABLE_NAME}` (`event_id`)" + }, + { + "name": "index_SpeakerWithEvent_speaker_id", + "unique": false, + "columnNames": [ + "speaker_id" + ], + "createSql": "CREATE INDEX `index_SpeakerWithEvent_speaker_id` ON `${TABLE_NAME}` (`speaker_id`)" + } + ], + "foreignKeys": [ + { + "table": "Event", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "event_id" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "Speaker", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "speaker_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "Sponsor", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, `description` TEXT, `url` TEXT, `logoUrl` TEXT, `level` INTEGER NOT NULL, `type` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "logoUrl", + "columnName": "logoUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "level", + "columnName": "level", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SponsorWithEvent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`event_id` INTEGER NOT NULL, `sponsor_id` INTEGER NOT NULL, PRIMARY KEY(`event_id`, `sponsor_id`), FOREIGN KEY(`event_id`) REFERENCES `Event`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`sponsor_id`) REFERENCES `Sponsor`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "eventId", + "columnName": "event_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sponsorId", + "columnName": "sponsor_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "event_id", + "sponsor_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_SponsorWithEvent_event_id", + "unique": false, + "columnNames": [ + "event_id" + ], + "createSql": "CREATE INDEX `index_SponsorWithEvent_event_id` ON `${TABLE_NAME}` (`event_id`)" + }, + { + "name": "index_SponsorWithEvent_sponsor_id", + "unique": false, + "columnNames": [ + "sponsor_id" + ], + "createSql": "CREATE INDEX `index_SponsorWithEvent_sponsor_id` ON `${TABLE_NAME}` (`sponsor_id`)" + } + ], + "foreignKeys": [ + { + "table": "Event", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "event_id" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "Sponsor", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "sponsor_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"f2497951a09fbddef232913f8b311f06\")" + ] + } +} \ No newline at end of file diff --git a/app/src/fdroid/java/org/fossasia/openevent/general/welcome/WelcomeFragment.kt b/app/src/fdroid/java/org/fossasia/openevent/general/welcome/WelcomeFragment.kt index fac9a699a..639dd9069 100644 --- a/app/src/fdroid/java/org/fossasia/openevent/general/welcome/WelcomeFragment.kt +++ b/app/src/fdroid/java/org/fossasia/openevent/general/welcome/WelcomeFragment.kt @@ -6,11 +6,10 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment -import androidx.navigation.Navigation +import androidx.navigation.fragment.findNavController import kotlinx.android.synthetic.main.fragment_welcome.view.currentLocation import kotlinx.android.synthetic.main.fragment_welcome.view.pickCityButton import org.fossasia.openevent.general.R -import org.fossasia.openevent.general.utils.Utils class WelcomeFragment : Fragment() { private lateinit var rootView: View @@ -23,7 +22,7 @@ class WelcomeFragment : Fragment() { rootView.currentLocation.visibility = View.GONE rootView.pickCityButton.setOnClickListener { - Navigation.findNavController(rootView).navigate(R.id.searchLocationFragment, null, Utils.getAnimSlide()) + findNavController().navigate(WelcomeFragmentDirections.actionWelcomeToSearch()) } return rootView diff --git a/app/src/main/java/org/fossasia/openevent/general/RotateBitmap.kt b/app/src/main/java/org/fossasia/openevent/general/RotateBitmap.kt new file mode 100644 index 000000000..a7344c7a5 --- /dev/null +++ b/app/src/main/java/org/fossasia/openevent/general/RotateBitmap.kt @@ -0,0 +1,114 @@ +package org.fossasia.openevent.general + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Matrix +import android.net.Uri +import androidx.exifinterface.media.ExifInterface +import java.io.IOException + +/** + * Handle the image rotation + */ + +class RotateBitmap { + + private var context: Context? = null + + /** + * This method is responsible for solving the rotation issue if exist. Also scale the images to + * 1024x1024 resolution + * + * @param selectedImage The Image URI + * @return Bitmap image results + * @throws IOException + */ + @Throws(IOException::class) + fun handleSamplingAndRotationBitmap(context: Context, selectedImage: Uri): Bitmap? { + this.context = context + val MAX_HEIGHT = 1024 + val MAX_WIDTH = 1024 + + // First decode with inJustDecodeBounds=true to check dimensions + val options = BitmapFactory.Options() + options.inJustDecodeBounds = true + var imageStream = context.contentResolver.openInputStream(selectedImage) + BitmapFactory.decodeStream(imageStream, null, options) + imageStream?.close() + + // Calculate inSampleSize + options.inSampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT) + + // Decode bitmap with inSampleSize set + options.inJustDecodeBounds = false + imageStream = context.contentResolver.openInputStream(selectedImage) + var img = BitmapFactory.decodeStream(imageStream, null, options) + + img = rotateImageIfRequired(img, selectedImage) + return img + } + + @Throws(IOException::class) + private fun rotateImageIfRequired(img: Bitmap?, selectedImage: Uri): Bitmap? { + + val input = context?.contentResolver?.openInputStream(selectedImage) + val ei = input?.let { ExifInterface(it) } + val orientation = ei?.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL) + return when (orientation) { + ExifInterface.ORIENTATION_ROTATE_90 -> rotateImage(img, 90) + ExifInterface.ORIENTATION_ROTATE_180 -> rotateImage(img, 180) + ExifInterface.ORIENTATION_ROTATE_270 -> rotateImage(img, 270) + else -> img + } + } + + companion object { + + private fun rotateImage(img: Bitmap?, degree: Int): Bitmap? { + val matrix = Matrix() + matrix.postRotate(degree.toFloat()) + val rotatedImg = img?.let { Bitmap.createBitmap(it, 0, 0, img.width, img.height, matrix, true) } + img?.recycle() + return rotatedImg + } + + private fun calculateInSampleSize( + options: BitmapFactory.Options, + reqWidth: Int, + reqHeight: Int + ): Int { + // Raw height and width of image + val height = options.outHeight + val width = options.outWidth + var inSampleSize = 1 + + if (height > reqHeight || width > reqWidth) { + + // Calculate ratios of height and width to requested height and width + val heightRatio = Math.round(height.toFloat() / reqHeight.toFloat()) + val widthRatio = Math.round(width.toFloat() / reqWidth.toFloat()) + + // Choose the smallest ratio as inSampleSize value, this will guarantee a final image + // with both dimensions larger than or equal to the requested height and width. + inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio + + // This offers some additional logic in case the image has a strange + // aspect ratio. For example, a panorama may have a much larger + // width than height. In these cases the total pixels might still + // end up being too large to fit comfortably in memory, so we should + // be more aggressive with sample down the image (=larger inSampleSize). + + val totalPixels = (width * height).toFloat() + + // Anything more than 2x the requested pixels we'll sample down further + val totalReqPixelsCap = (reqWidth * reqHeight * 2).toFloat() + + while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) { + inSampleSize++ + } + } + return inSampleSize + } + } +} diff --git a/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeFragment.kt b/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeFragment.kt index 6309b881e..7db26c9e0 100644 --- a/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/attendees/AttendeeFragment.kt @@ -71,11 +71,9 @@ import org.fossasia.openevent.general.event.Event import org.fossasia.openevent.general.event.EventId import org.fossasia.openevent.general.event.EventUtils import org.fossasia.openevent.general.order.Charge -import org.fossasia.openevent.general.order.OrderCompletedFragmentArgs import org.fossasia.openevent.general.ticket.TicketDetailsRecyclerAdapter import org.fossasia.openevent.general.ticket.TicketId import org.fossasia.openevent.general.utils.Utils -import org.fossasia.openevent.general.utils.Utils.getAnimFade import org.fossasia.openevent.general.utils.Utils.isNetworkConnected import org.fossasia.openevent.general.utils.extensions.nonNull import org.fossasia.openevent.general.utils.nullToEmpty @@ -555,11 +553,8 @@ class AttendeeFragment : Fragment() { private fun openOrderCompletedFragment() { attendeeViewModel.paymentCompleted.value = false - OrderCompletedFragmentArgs(safeArgs.eventId) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.orderCompletedFragment, bundle, getAnimFade()) - } + findNavController(rootView).navigate(AttendeeFragmentDirections + .actionAttendeeToOrderCompleted(safeArgs.eventId)) } override fun onOptionsItemSelected(item: MenuItem): Boolean { diff --git a/app/src/main/java/org/fossasia/openevent/general/auth/EditProfileFragment.kt b/app/src/main/java/org/fossasia/openevent/general/auth/EditProfileFragment.kt index dab33d84f..4e7124255 100644 --- a/app/src/main/java/org/fossasia/openevent/general/auth/EditProfileFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/auth/EditProfileFragment.kt @@ -6,8 +6,6 @@ import androidx.appcompat.app.AlertDialog import android.content.Intent import android.content.pm.PackageManager import android.graphics.Bitmap -import android.graphics.BitmapFactory -import android.net.Uri import android.os.Bundle import android.util.Base64 import android.view.LayoutInflater @@ -23,6 +21,7 @@ import com.squareup.picasso.Picasso import kotlinx.android.synthetic.main.fragment_edit_profile.view.editProfileCoordinatorLayout import kotlinx.android.synthetic.main.fragment_edit_profile.view.updateButton import kotlinx.android.synthetic.main.fragment_edit_profile.view.firstName +import com.squareup.picasso.MemoryPolicy import kotlinx.android.synthetic.main.fragment_edit_profile.view.lastName import kotlinx.android.synthetic.main.fragment_edit_profile.view.profilePhoto import kotlinx.android.synthetic.main.fragment_edit_profile.view.progressBar @@ -30,6 +29,7 @@ import kotlinx.android.synthetic.main.fragment_edit_profile.view.profilePhotoFab import org.fossasia.openevent.general.CircleTransform import org.fossasia.openevent.general.MainActivity import org.fossasia.openevent.general.R +import org.fossasia.openevent.general.RotateBitmap import org.fossasia.openevent.general.utils.Utils.hideSoftKeyboard import org.fossasia.openevent.general.utils.Utils.requireDrawable import org.fossasia.openevent.general.utils.extensions.nonNull @@ -37,6 +37,9 @@ import org.fossasia.openevent.general.utils.nullToEmpty import org.koin.androidx.viewmodel.ext.android.viewModel import timber.log.Timber import java.io.ByteArrayOutputStream +import java.io.File +import java.io.FileOutputStream +import java.io.IOException import java.io.FileNotFoundException import org.fossasia.openevent.general.utils.Utils.setToolbar import org.jetbrains.anko.design.snackbar @@ -47,14 +50,12 @@ class EditProfileFragment : Fragment() { private val editProfileViewModel by viewModel() private lateinit var rootView: View private var permissionGranted = false - private var encodedImage: String? = null private val PICK_IMAGE_REQUEST = 100 private val READ_STORAGE = arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE) private val REQUEST_CODE = 1 private lateinit var userFirstName: String private lateinit var userLastName: String - private var avatarUpdated: Boolean = false override fun onCreateView( inflater: LayoutInflater, @@ -75,7 +76,7 @@ class EditProfileFragment : Fragment() { if (rootView.lastName.text.isNullOrBlank()) { rootView.lastName.setText(userLastName) } - if (!imageUrl.isEmpty() && !avatarUpdated) { + if (imageUrl.isNotEmpty() && !editProfileViewModel.avatarUpdated) { val drawable = requireDrawable(requireContext(), R.drawable.ic_account_circle_grey) Picasso.get() .load(imageUrl) @@ -84,16 +85,7 @@ class EditProfileFragment : Fragment() { .into(rootView.profilePhoto) } }) - profileViewModel.avatarPicked.observe(this, Observer { - if (it != null) { - Picasso.get() - .load(Uri.parse(it)) - .placeholder(requireDrawable(requireContext(), R.drawable.ic_account_circle_grey)) - .transform(CircleTransform()) - .into(rootView.profilePhoto) - this.avatarUpdated = true - } - }) + profileViewModel.fetchProfile() editProfileViewModel.progress @@ -102,12 +94,25 @@ class EditProfileFragment : Fragment() { rootView.progressBar.isVisible = it }) + editProfileViewModel.getUpdatedTempFile() + .nonNull() + .observe(viewLifecycleOwner, Observer { file -> + // prevent picasso from storing tempAvatar cache, + // if user select another image picasso will display tempAvatar instead of its own cache + Picasso.get() + .load(file) + .placeholder(requireDrawable(requireContext(), R.drawable.ic_person_black)) + .memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE) + .transform(CircleTransform()) + .into(rootView.profilePhoto) + }) + permissionGranted = (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) rootView.updateButton.setOnClickListener { hideSoftKeyboard(context, rootView) - editProfileViewModel.updateProfile(encodedImage, rootView.firstName.text.toString(), + editProfileViewModel.updateProfile(rootView.firstName.text.toString(), rootView.lastName.text.toString()) } @@ -138,20 +143,12 @@ class EditProfileFragment : Fragment() { val imageUri = intentData.data ?: return try { - val imageStream = activity?.contentResolver?.openInputStream(imageUri) - val selectedImage = BitmapFactory.decodeStream(imageStream) - encodedImage = encodeImage(selectedImage) + val selectedImage = RotateBitmap().handleSamplingAndRotationBitmap(requireContext(), imageUri) + editProfileViewModel.encodedImage = selectedImage?.let { encodeImage(it) } + editProfileViewModel.avatarUpdated = true } catch (e: FileNotFoundException) { Timber.d(e, "File Not Found Exception") } - - Picasso.get() - .load(imageUri) - .placeholder(requireDrawable(requireContext(), R.drawable.ic_person_black)) - .transform(CircleTransform()) - .into(rootView.profilePhoto) - avatarUpdated = true - profileViewModel.avatarPicked.value = imageUri.toString() } } @@ -160,6 +157,23 @@ class EditProfileFragment : Fragment() { bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos) val bytes = baos.toByteArray() + //create temp file + try { + + val tempAvatar = File(context?.cacheDir, "tempAvatar") + if (tempAvatar.exists()) { + tempAvatar.delete() + } + val fos = FileOutputStream(tempAvatar) + fos.write(bytes) + fos.flush() + fos.close() + + editProfileViewModel.setUpdatedTempFile(tempAvatar) + } catch (e: IOException) { + e.printStackTrace() + } + return "data:image/jpeg;base64," + Base64.encodeToString(bytes, Base64.DEFAULT) } @@ -207,7 +221,7 @@ class EditProfileFragment : Fragment() { */ fun handleBackPress() { val thisActivity = activity - if (!avatarUpdated && rootView.lastName.text.toString() == userLastName && + if (!editProfileViewModel.avatarUpdated && rootView.lastName.text.toString() == userLastName && rootView.firstName.text.toString() == userFirstName) { if (thisActivity is MainActivity) thisActivity.onSuperBackPressed() } else { @@ -218,7 +232,7 @@ class EditProfileFragment : Fragment() { if (thisActivity is MainActivity) thisActivity.onSuperBackPressed() } dialog.setPositiveButton(getString(R.string.save)) { _, _ -> - editProfileViewModel.updateProfile(encodedImage, rootView.firstName.text.toString(), + editProfileViewModel.updateProfile(rootView.firstName.text.toString(), rootView.lastName.text.toString()) } dialog.create().show() diff --git a/app/src/main/java/org/fossasia/openevent/general/auth/EditProfileViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/auth/EditProfileViewModel.kt index 89f9458f2..6cd173c57 100644 --- a/app/src/main/java/org/fossasia/openevent/general/auth/EditProfileViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/auth/EditProfileViewModel.kt @@ -10,6 +10,7 @@ import org.fossasia.openevent.general.R import org.fossasia.openevent.general.common.SingleLiveEvent import org.fossasia.openevent.general.data.Resource import timber.log.Timber +import java.io.File class EditProfileViewModel( private val authService: AuthService, @@ -25,10 +26,17 @@ class EditProfileViewModel( val user: LiveData = mutableUser private val mutableMessage = SingleLiveEvent() val message: LiveData = mutableMessage + private var updatedImageTemp = MutableLiveData() + var avatarUpdated = false + var encodedImage: String? = null fun isLoggedIn() = authService.isLoggedIn() - fun updateProfile(encodedImage: String?, firstName: String, lastName: String) { + /** + * @param firstName updated firstName + * @param lastName updated lastName + */ + fun updateProfile(firstName: String, lastName: String) { if (encodedImage.isNullOrEmpty()) { updateUser(null, firstName, lastName) return @@ -51,7 +59,7 @@ class EditProfileViewModel( } } - fun updateUser(url: String?, firstName: String, lastName: String) { + private fun updateUser(url: String?, firstName: String, lastName: String) { val id = authHolder.getId() if (firstName.isEmpty() || lastName.isEmpty()) { mutableMessage.value = resource.getString(R.string.provide_name_message) @@ -81,6 +89,14 @@ class EditProfileViewModel( } } + fun setUpdatedTempFile(file: File) { + updatedImageTemp.value = file + } + + fun getUpdatedTempFile(): MutableLiveData { + return updatedImageTemp + } + override fun onCleared() { super.onCleared() compositeDisposable.clear() diff --git a/app/src/main/java/org/fossasia/openevent/general/auth/ProfileFragment.kt b/app/src/main/java/org/fossasia/openevent/general/auth/ProfileFragment.kt index b5056bf1f..d3a4b73f0 100644 --- a/app/src/main/java/org/fossasia/openevent/general/auth/ProfileFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/auth/ProfileFragment.kt @@ -25,9 +25,7 @@ import kotlinx.android.synthetic.main.fragment_profile.view.settingsLL import kotlinx.android.synthetic.main.fragment_profile.view.ticketIssuesLL import org.fossasia.openevent.general.CircleTransform import org.fossasia.openevent.general.R -import org.fossasia.openevent.general.settings.SettingsFragmentArgs import org.fossasia.openevent.general.utils.Utils -import org.fossasia.openevent.general.utils.Utils.getAnimFade import org.fossasia.openevent.general.utils.Utils.requireDrawable import org.fossasia.openevent.general.utils.extensions.nonNull import org.fossasia.openevent.general.utils.nullToEmpty @@ -42,11 +40,9 @@ class ProfileFragment : Fragment() { private var emailSettings: String? = null private fun redirectToLogin() { - LoginFragmentArgs(getString(R.string.log_in_first)) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.loginFragment, bundle, getAnimFade()) - } + findNavController(rootView).navigate(ProfileFragmentDirections + .actionProfileToLogin(getString(R.string.log_in_first)) + ) } private fun redirectToEventsFragment() { @@ -93,7 +89,7 @@ class ProfileFragment : Fragment() { .into(rootView.avatar) rootView.editProfileRL.setOnClickListener { - findNavController(rootView).navigate(R.id.editProfileFragment, null, getAnimFade()) + findNavController(rootView).navigate(ProfileFragmentDirections.actionProfileToEditProfile()) } }) @@ -102,11 +98,7 @@ class ProfileFragment : Fragment() { rootView.manageEventsLL.setOnClickListener { startOrgaApp("com.eventyay.organizer") } rootView.settingsLL.setOnClickListener { - SettingsFragmentArgs(emailSettings) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.settingsFragment, bundle, getAnimFade()) - } + findNavController(rootView).navigate(ProfileFragmentDirections.actionProfileToSettings(emailSettings)) } rootView.ticketIssuesLL.setOnClickListener { diff --git a/app/src/main/java/org/fossasia/openevent/general/auth/ProfileViewModel.kt b/app/src/main/java/org/fossasia/openevent/general/auth/ProfileViewModel.kt index f14f6d6b3..0ac51f36e 100644 --- a/app/src/main/java/org/fossasia/openevent/general/auth/ProfileViewModel.kt +++ b/app/src/main/java/org/fossasia/openevent/general/auth/ProfileViewModel.kt @@ -21,7 +21,6 @@ class ProfileViewModel(private val authService: AuthService, private val resourc val user: LiveData = mutableUser private val mutableError = SingleLiveEvent() val error: LiveData = mutableError - val avatarPicked = MutableLiveData() fun isLoggedIn() = authService.isLoggedIn() diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt index 9622bc89e..b0ec40332 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventDetailsFragment.kt @@ -55,24 +55,17 @@ import kotlinx.android.synthetic.main.dialog_feedback.view.feedbackTextInputLayo import kotlinx.android.synthetic.main.dialog_feedback.view.feedbackrating import kotlinx.android.synthetic.main.fragment_event.eventCoordinatorLayout import org.fossasia.openevent.general.R -import org.fossasia.openevent.general.about.AboutEventFragmentArgs -import org.fossasia.openevent.general.auth.LoginFragmentArgs import org.fossasia.openevent.general.common.SpeakerClickListener import org.fossasia.openevent.general.databinding.FragmentEventBinding import org.fossasia.openevent.general.event.EventUtils.loadMapUrl -import org.fossasia.openevent.general.event.faq.EventFAQFragmentArgs import org.fossasia.openevent.general.event.feedback.FeedbackRecyclerAdapter import org.fossasia.openevent.general.event.topic.SimilarEventsFragment import org.fossasia.openevent.general.sessions.SessionRecyclerAdapter import org.fossasia.openevent.general.social.SocialLinksFragment -import org.fossasia.openevent.general.speakers.SpeakerFragmentArgs import org.fossasia.openevent.general.speakers.SpeakerRecyclerAdapter import org.fossasia.openevent.general.sponsor.SponsorClickListener import org.fossasia.openevent.general.sponsor.SponsorRecyclerAdapter -import org.fossasia.openevent.general.sponsor.SponsorsFragmentArgs -import org.fossasia.openevent.general.ticket.TicketsFragmentArgs import org.fossasia.openevent.general.utils.Utils -import org.fossasia.openevent.general.utils.Utils.getAnimSlide import org.fossasia.openevent.general.utils.extensions.nonNull import org.fossasia.openevent.general.utils.nullToEmpty import org.fossasia.openevent.general.utils.stripHtml @@ -181,11 +174,8 @@ class EventDetailsFragment : Fragment() { val speakerClickListener: SpeakerClickListener = object : SpeakerClickListener { override fun onClick(speakerId: Long) { - SpeakerFragmentArgs(speakerId) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.speakerFragment, bundle, getAnimSlide()) - } + findNavController(rootView).navigate(EventDetailsFragmentDirections + .actionEventDetailsToSpeaker(speakerId)) } } @@ -297,11 +287,8 @@ class EventDetailsFragment : Fragment() { currency = Currency.getInstance(event.paymentCurrency ?: "USD").symbol // About event on-click val aboutEventOnClickListener = View.OnClickListener { - AboutEventFragmentArgs(safeArgs.eventId) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.aboutEventFragment, bundle, getAnimSlide()) - } + findNavController(rootView).navigate(EventDetailsFragmentDirections + .actionEventDetailsToAboutEvent(safeArgs.eventId)) } // Event Description Section @@ -380,11 +367,8 @@ class EventDetailsFragment : Fragment() { return true } R.id.open_faqs -> { - EventFAQFragmentArgs(safeArgs.eventId) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.eventFAQFragment, bundle, getAnimSlide()) - } + findNavController(rootView).navigate(EventDetailsFragmentDirections + .actionEventDetailsToFaq(safeArgs.eventId)) return true } else -> super.onOptionsItemSelected(item) @@ -432,11 +416,8 @@ class EventDetailsFragment : Fragment() { } private fun loadTicketFragment() { - TicketsFragmentArgs(safeArgs.eventId, currency) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.ticketsFragment, bundle, getAnimSlide()) - } + findNavController(rootView).navigate(EventDetailsFragmentDirections + .actionEventDetailsToTickets(safeArgs.eventId, currency)) } private fun loadSocialLinksFragment() { @@ -504,11 +485,8 @@ class EventDetailsFragment : Fragment() { } private fun redirectToLogin() { - LoginFragmentArgs(getString(R.string.log_in_first)) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.loginFragment, bundle, Utils.getAnimFade()) - } + findNavController(rootView).navigate(EventDetailsFragmentDirections + .actionEventDetailsToLogin(getString(R.string.log_in_first))) } private fun writeFeedback() { @@ -548,11 +526,8 @@ class EventDetailsFragment : Fragment() { } private fun moveToSponsorSection() { - SponsorsFragmentArgs(safeArgs.eventId) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.sponsorsFragment, bundle, getAnimSlide()) - } + findNavController(rootView).navigate(EventDetailsFragmentDirections + .actionEventDetailsToSponsor(safeArgs.eventId)) } override fun onDestroy() { diff --git a/app/src/main/java/org/fossasia/openevent/general/event/EventsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/event/EventsFragment.kt index 11e7b5c81..52ad92b7c 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/EventsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/EventsFragment.kt @@ -31,9 +31,6 @@ import org.fossasia.openevent.general.common.FavoriteFabClickListener import org.fossasia.openevent.general.data.Preference import org.fossasia.openevent.general.di.Scopes import org.fossasia.openevent.general.search.SAVED_LOCATION -import org.fossasia.openevent.general.search.SearchResultsFragmentArgs -import org.fossasia.openevent.general.utils.Utils.getAnimFade -import org.fossasia.openevent.general.utils.Utils.getAnimSlide import org.fossasia.openevent.general.utils.extensions.nonNull import org.koin.android.ext.android.inject import org.koin.androidx.scope.ext.android.bindScope @@ -140,7 +137,7 @@ class EventsFragment : Fragment(), ScrollToTop { }) rootView.locationTextView.setOnClickListener { - findNavController(rootView).navigate(R.id.searchLocationFragment, null, getAnimSlide()) + findNavController(rootView).navigate(EventsFragmentDirections.actionEventsToSearchLocation()) } rootView.retry.setOnClickListener { @@ -170,11 +167,7 @@ class EventsFragment : Fragment(), ScrollToTop { val eventClickListener: EventClickListener = object : EventClickListener { override fun onClick(eventID: Long) { - EventDetailsFragmentArgs(eventID) - .toBundle() - .also { bundle -> - findNavController(view).navigate(R.id.eventDetailsFragment, bundle, getAnimFade()) - } + findNavController(view).navigate(EventsFragmentDirections.actionEventsToEventsDetail(eventID)) } } @@ -200,16 +193,11 @@ class EventsFragment : Fragment(), ScrollToTop { } private fun openSearch(hashTag: String) { - SearchResultsFragmentArgs( - query = "", - location = Preference().getString(SAVED_LOCATION).toString(), - date = getString(R.string.anytime), - type = hashTag - ) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.searchResultsFragment, bundle, getAnimSlide()) - } + findNavController(rootView).navigate(EventsFragmentDirections.actionEventsToSearchResults( + query = "", + location = Preference().getString(SAVED_LOCATION).toString(), + date = getString(R.string.anytime), + type = hashTag)) } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { diff --git a/app/src/main/java/org/fossasia/openevent/general/event/topic/SimilarEventsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/event/topic/SimilarEventsFragment.kt index 6c721db57..9b63a6516 100644 --- a/app/src/main/java/org/fossasia/openevent/general/event/topic/SimilarEventsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/event/topic/SimilarEventsFragment.kt @@ -19,11 +19,10 @@ import org.fossasia.openevent.general.R import org.fossasia.openevent.general.di.Scopes import org.fossasia.openevent.general.event.Event import org.fossasia.openevent.general.common.EventClickListener -import org.fossasia.openevent.general.event.EventDetailsFragmentArgs import org.fossasia.openevent.general.event.EventsListAdapter import org.fossasia.openevent.general.common.FavoriteFabClickListener +import org.fossasia.openevent.general.event.EventDetailsFragmentDirections import org.fossasia.openevent.general.event.EventLayoutType -import org.fossasia.openevent.general.utils.Utils.getAnimSlide import org.fossasia.openevent.general.utils.extensions.nonNull import org.koin.android.ext.android.get import org.koin.androidx.scope.ext.android.bindScope @@ -97,12 +96,8 @@ class SimilarEventsFragment : Fragment() { val eventClickListener: EventClickListener = object : EventClickListener { override fun onClick(eventID: Long) { - EventDetailsFragmentArgs(eventID) - .toBundle() - .also { bundle -> - findNavController(view).navigate(R.id.eventDetailsFragment, bundle, - getAnimSlide()) - } + findNavController(view).navigate(EventDetailsFragmentDirections + .actionSimilarEventsToEventDetails(eventID)) } } diff --git a/app/src/main/java/org/fossasia/openevent/general/favorite/FavoriteFragment.kt b/app/src/main/java/org/fossasia/openevent/general/favorite/FavoriteFragment.kt index 01a35e1fd..81d8e4ba1 100644 --- a/app/src/main/java/org/fossasia/openevent/general/favorite/FavoriteFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/favorite/FavoriteFragment.kt @@ -7,7 +7,6 @@ import android.view.ViewGroup import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.lifecycle.Observer -import androidx.navigation.NavOptions import androidx.recyclerview.widget.LinearLayoutManager import androidx.navigation.Navigation.findNavController import kotlinx.android.synthetic.main.fragment_favorite.noLikedLL @@ -23,13 +22,9 @@ import org.fossasia.openevent.general.R import org.fossasia.openevent.general.di.Scopes import org.fossasia.openevent.general.event.Event import org.fossasia.openevent.general.common.EventClickListener -import org.fossasia.openevent.general.event.EventDetailsFragmentArgs import org.fossasia.openevent.general.common.FavoriteFabClickListener import org.fossasia.openevent.general.data.Preference import org.fossasia.openevent.general.search.SAVED_LOCATION -import org.fossasia.openevent.general.search.SearchResultsFragmentArgs -import org.fossasia.openevent.general.utils.Utils -import org.fossasia.openevent.general.utils.Utils.getAnimFade import org.fossasia.openevent.general.utils.extensions.nonNull import org.koin.android.ext.android.inject import org.koin.androidx.scope.ext.android.bindScope @@ -66,14 +61,7 @@ class FavoriteFragment : Fragment() { setToolbar(activity, getString(R.string.likes), false) rootView.findText.setOnClickListener { - val navOptions = NavOptions.Builder() - .setPopUpTo(R.id.eventsFragment, false) - .setEnterAnim(R.anim.slide_in_right) - .setExitAnim(R.anim.slide_out_left) - .setPopEnterAnim(R.anim.slide_in_left) - .setPopExitAnim(R.anim.slide_out_right) - .build() - findNavController(rootView).navigate(R.id.searchFragment, null, navOptions) + findNavController(rootView).navigate(FavoriteFragmentDirections.actionFavouriteToSearch()) } rootView.todayChip.setOnClickListener { @@ -119,11 +107,7 @@ class FavoriteFragment : Fragment() { val eventClickListener: EventClickListener = object : EventClickListener { override fun onClick(eventID: Long) { - EventDetailsFragmentArgs(eventID) - .toBundle() - .also { bundle -> - findNavController(view).navigate(R.id.eventDetailsFragment, bundle, getAnimFade()) - } + findNavController(rootView).navigate(FavoriteFragmentDirections.actionFavouriteToEventDetails(eventID)) } } @@ -150,13 +134,10 @@ class FavoriteFragment : Fragment() { } private fun openSearchResult(time: String) { - SearchResultsFragmentArgs(query = "", + findNavController(rootView).navigate(FavoriteFragmentDirections.actionFavouriteToSearchResults(query = "", location = Preference().getString(SAVED_LOCATION).toString(), type = getString(R.string.anything), - date = time) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.searchResultsFragment, bundle, Utils.getAnimSlide()) - } + date = time + )) } } diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrderCompletedFragment.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrderCompletedFragment.kt index 6dad6bf6f..5e5679d26 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrderCompletedFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrderCompletedFragment.kt @@ -11,8 +11,6 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.lifecycle.Observer -import androidx.navigation.NavOptions -import androidx.navigation.Navigation import androidx.navigation.Navigation.findNavController import androidx.navigation.fragment.navArgs import kotlinx.android.synthetic.main.fragment_order.view.orderCoordinatorLayout @@ -118,8 +116,7 @@ class OrderCompletedFragment : Fragment() { } private fun openTicketDetails() { - val navOptions = NavOptions.Builder().setPopUpTo(R.id.eventsFragment, false).build() - Navigation.findNavController(rootView).navigate(R.id.orderUnderUserFragment, null, navOptions) + findNavController(rootView).navigate(OrderCompletedFragmentDirections.actionOrderCompletedToOrderUser()) } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsFragment.kt index dea800782..7d31a77ce 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsFragment.kt @@ -26,8 +26,6 @@ import kotlinx.android.synthetic.main.fragment_order_details.view.progressBar import kotlinx.android.synthetic.main.item_card_order_details.view.orderDetailCardView import org.fossasia.openevent.general.BuildConfig import org.fossasia.openevent.general.R -import org.fossasia.openevent.general.event.EventDetailsFragmentArgs -import org.fossasia.openevent.general.utils.Utils.getAnimFade import org.fossasia.openevent.general.utils.extensions.nonNull import org.koin.androidx.viewmodel.ext.android.viewModel import timber.log.Timber @@ -99,11 +97,8 @@ class OrderDetailsFragment : Fragment() { val eventDetailsListener = object : OrderDetailsRecyclerAdapter.EventDetailsListener { override fun onClick(eventID: Long) { - EventDetailsFragmentArgs(eventID) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.eventDetailsFragment, bundle, getAnimFade()) - } + findNavController(rootView).navigate(OrderDetailsFragmentDirections + .actionOrderDetailsToEventDetails(eventID)) } } diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsViewHolder.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsViewHolder.kt index bb1b18dda..c81461800 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsViewHolder.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrderDetailsViewHolder.kt @@ -87,9 +87,10 @@ class OrderDetailsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) } itemView.name.text = "${attendee.firstname} ${attendee.lastname}" - itemView.orderIdentifier.text = orderIdentifier + val ticketIdentifier = "$orderIdentifier-${attendee.id}" + itemView.orderIdentifier.text = ticketIdentifier + val bitmap = qrCode.generateQrBitmap(ticketIdentifier, 200, 200) - val bitmap = qrCode.generateQrBitmap(orderIdentifier, 200, 200) if (bitmap != null) { itemView.qrCodeView.setImageBitmap(bitmap) } else { diff --git a/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserFragment.kt b/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserFragment.kt index 4be4b92c7..196c9e868 100644 --- a/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/order/OrdersUnderUserFragment.kt @@ -23,11 +23,8 @@ import kotlinx.android.synthetic.main.fragment_orders_under_user.view.ordersNest import kotlinx.android.synthetic.main.fragment_orders_under_user.view.expireFilter import org.fossasia.openevent.general.R import org.fossasia.openevent.general.ScrollToTop -import org.fossasia.openevent.general.auth.LoginFragmentArgs import org.fossasia.openevent.general.event.EventUtils import org.fossasia.openevent.general.utils.Utils -import org.fossasia.openevent.general.utils.Utils.getAnimFade -import org.fossasia.openevent.general.utils.Utils.getAnimSlide import org.fossasia.openevent.general.utils.Utils.navAnimGone import org.fossasia.openevent.general.utils.extensions.nonNull import org.koin.androidx.viewmodel.ext.android.viewModel @@ -81,20 +78,15 @@ class OrdersUnderUserFragment : Fragment(), ScrollToTop { val recyclerViewClickListener = object : OrdersRecyclerAdapter.OrderClickListener { override fun onClick(eventID: Long, orderIdentifier: String) { - OrderDetailsFragmentArgs(eventID, orderIdentifier) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.orderDetailsFragment, bundle, getAnimFade()) - } + findNavController(rootView).navigate(OrdersUnderUserFragmentDirections + .actionOrderUserToOrderDetails(eventID, orderIdentifier)) } } rootView.expireFilter.setOnClickListener { - OrdersUnderUserFragmentArgs(showExpired = true) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.orderUnderUserFragment, bundle, getAnimSlide()) - } + findNavController(rootView).navigate(OrdersUnderUserFragmentDirections + .actionOrderUserToOrderUserExpire(showExpired = true) + ) } ordersRecyclerAdapter.setListener(recyclerViewClickListener) @@ -151,11 +143,8 @@ class OrdersUnderUserFragment : Fragment(), ScrollToTop { } private fun redirectToLogin() { - LoginFragmentArgs(getString(R.string.log_in_first)) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.loginFragment, bundle, getAnimFade()) - } + findNavController(rootView).navigate(OrdersUnderUserFragmentDirections + .actionOrderUserToLogin(getString(R.string.log_in_first))) } override fun scrollToTop() = rootView.ordersNestedScrollView.smoothScrollTo(0, 0) diff --git a/app/src/main/java/org/fossasia/openevent/general/search/SearchFilterFragment.kt b/app/src/main/java/org/fossasia/openevent/general/search/SearchFilterFragment.kt index 82f90dc8e..36e715414 100644 --- a/app/src/main/java/org/fossasia/openevent/general/search/SearchFilterFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/search/SearchFilterFragment.kt @@ -9,7 +9,7 @@ import android.view.View import android.view.ViewGroup import android.widget.RadioButton import androidx.fragment.app.Fragment -import androidx.navigation.Navigation +import androidx.navigation.Navigation.findNavController import androidx.navigation.fragment.navArgs import kotlinx.android.synthetic.main.fragment_search_filter.view.dateRadioButton import kotlinx.android.synthetic.main.fragment_search_filter.view.freeStuffCheckBox @@ -19,7 +19,6 @@ import kotlinx.android.synthetic.main.fragment_search_filter.view.tvSelectCatego import kotlinx.android.synthetic.main.fragment_search_filter.view.tvSelectDate import kotlinx.android.synthetic.main.fragment_search_filter.view.tvSelectLocation import org.fossasia.openevent.general.R -import org.fossasia.openevent.general.utils.Utils.getAnimFade import org.fossasia.openevent.general.utils.Utils.setToolbar import org.koin.androidx.viewmodel.ext.android.viewModel @@ -77,20 +76,14 @@ class SearchFilterFragment : Fragment() { true } R.id.filter_set -> { - val navigator = Navigation.findNavController(rootView) - navigator.popBackStack(R.id.searchResultsFragment, true) - SearchResultsFragmentArgs( + findNavController(rootView).navigate(SearchFilterFragmentDirections.actionSearchFilterToSearchResults( date = selectedTime, freeEvents = isFreeStuffChecked, location = selectedLocation, type = selectedCategory, query = safeArgs.query, sort = sortBy - ) - .toBundle() - .also { - navigator.navigate(R.id.searchResultsFragment, it, getAnimFade()) - } + )) true } else -> super.onOptionsItemSelected(item) @@ -111,44 +104,28 @@ class SearchFilterFragment : Fragment() { rootView.tvSelectDate.text = selectedTime rootView.tvSelectDate.setOnClickListener { - SearchTimeFragmentArgs( + findNavController(rootView).navigate(SearchFilterFragmentDirections.actionSearchFilterToSearchTime( time = selectedTime, fromFragmentName = SEARCH_FILTER_FRAGMENT, query = safeArgs.query - ) - .toBundle() - .also { - Navigation.findNavController(rootView) - .navigate(R.id.searchTimeFragment, it, getAnimFade()) - } + )) } rootView.tvSelectLocation.text = selectedLocation rootView.tvSelectLocation.setOnClickListener { - SearchTimeFragmentArgs( - time = selectedLocation, + findNavController(rootView).navigate(SearchFilterFragmentDirections.actionSearchFilterToSearchLocation( fromFragmentName = SEARCH_FILTER_FRAGMENT, query = safeArgs.query - ) - .toBundle() - .also { - Navigation.findNavController(rootView) - .navigate(R.id.searchLocationFragment, it, getAnimFade()) - } + )) } rootView.tvSelectCategory.text = selectedCategory rootView.tvSelectCategory.setOnClickListener { - SearchTimeFragmentArgs( - time = selectedCategory, - fromFragmentName = SEARCH_FILTER_FRAGMENT, - query = safeArgs.query - ) - .toBundle() - .also { - Navigation.findNavController(rootView) - .navigate(R.id.searchTypeFragment, it, getAnimFade()) - } + findNavController(rootView).navigate(SearchFilterFragmentDirections.actionSearchFilterToSearchType( + selectedCategory, + SEARCH_FILTER_FRAGMENT, + safeArgs.query + )) } rootView.freeStuffCheckBox.isChecked = safeArgs.freeEvents diff --git a/app/src/main/java/org/fossasia/openevent/general/search/SearchFragment.kt b/app/src/main/java/org/fossasia/openevent/general/search/SearchFragment.kt index cf9980a55..be408efcb 100644 --- a/app/src/main/java/org/fossasia/openevent/general/search/SearchFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/search/SearchFragment.kt @@ -9,7 +9,6 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.widget.SearchView import androidx.fragment.app.Fragment -import androidx.navigation.Navigation.findNavController import kotlinx.android.synthetic.main.fragment_search.view.fabSearch import kotlinx.android.synthetic.main.fragment_search.view.locationTextView import kotlinx.android.synthetic.main.fragment_search.view.timeTextView @@ -17,11 +16,10 @@ import kotlinx.android.synthetic.main.fragment_search.view.eventTypeTextView import org.fossasia.openevent.general.R import org.fossasia.openevent.general.utils.nullToEmpty import org.koin.androidx.viewmodel.ext.android.viewModel -import androidx.navigation.Navigation +import androidx.navigation.Navigation.findNavController import org.fossasia.openevent.general.MainActivity import org.fossasia.openevent.general.event.EventUtils.getFormattedDate import org.fossasia.openevent.general.event.EventUtils.getFormattedDateWithoutYear -import org.fossasia.openevent.general.utils.Utils.getAnimSlide import org.threeten.bp.LocalDate import org.threeten.bp.ZoneId import org.threeten.bp.format.DateTimeFormatter @@ -47,12 +45,10 @@ class SearchFragment : Fragment() { setHasOptionsMenu(true) rootView.timeTextView.setOnClickListener { - SearchTimeFragmentArgs(rootView.timeTextView.text.toString(), - fromFragmentName = SEARCH_FRAGMENT) - .toBundle() - .also { bundle -> - Navigation.findNavController(rootView).navigate(R.id.searchTimeFragment, bundle, getAnimSlide()) - } + findNavController(rootView).navigate(SearchFragmentDirections.actionSearchToSearchTime( + rootView.timeTextView.text.toString(), + fromFragmentName = SEARCH_FRAGMENT + )) } searchViewModel.loadSavedTime() val time = searchViewModel.savedTime @@ -77,19 +73,15 @@ class SearchFragment : Fragment() { rootView.locationTextView.text = searchViewModel.savedLocation rootView.locationTextView.setOnClickListener { - SearchLocationFragmentArgs(fromFragmentName = SEARCH_FRAGMENT) - .toBundle() - .also { bundle -> - Navigation.findNavController(rootView).navigate(R.id.searchLocationFragment, bundle, getAnimSlide()) - } + findNavController(rootView).navigate(SearchFragmentDirections.actionSearchToSearchLocation( + fromFragmentName = SEARCH_FRAGMENT + )) } rootView.eventTypeTextView.setOnClickListener { - SearchLocationFragmentArgs(fromFragmentName = SEARCH_FRAGMENT) - .toBundle() - .also { bundle -> - Navigation.findNavController(rootView).navigate(R.id.searchTypeFragment, bundle, getAnimSlide()) - } + findNavController(rootView).navigate(SearchFragmentDirections.actionSearchToSearchType( + fromFragmentName = SEARCH_FRAGMENT + )) } return rootView @@ -118,16 +110,12 @@ class SearchFragment : Fragment() { searchItem.actionView = searchView val queryListener = object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String): Boolean { - SearchResultsFragmentArgs( + findNavController(rootView).navigate(SearchFragmentDirections.actionSearchToSearchResults( query = query, location = rootView.locationTextView.text.toString().nullToEmpty(), date = (searchViewModel.savedTime ?: getString(R.string.anytime)).nullToEmpty(), type = (searchViewModel.savedType ?: getString(R.string.anything)).nullToEmpty() - ) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.searchResultsFragment, bundle, getAnimSlide()) - } + )) return false } diff --git a/app/src/main/java/org/fossasia/openevent/general/search/SearchLocationFragment.kt b/app/src/main/java/org/fossasia/openevent/general/search/SearchLocationFragment.kt index bf8fb539a..b5c54817f 100644 --- a/app/src/main/java/org/fossasia/openevent/general/search/SearchLocationFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/search/SearchLocationFragment.kt @@ -107,11 +107,11 @@ class SearchLocationFragment : Fragment() { private fun redirectToMain() { val fragmentId = when (safeArgs.fromFragmentName) { - SEARCH_FRAGMENT -> R.id.searchFragment - SEARCH_FILTER_FRAGMENT -> R.id.searchFilterFragment - else -> R.id.eventsFragment + SEARCH_FRAGMENT -> SearchLocationFragmentDirections.actionSearchLocationToSearch() + SEARCH_FILTER_FRAGMENT -> SearchLocationFragmentDirections.actionSearchLocationToSearchFilter() + else -> SearchLocationFragmentDirections.actionSearchLocationToEvents() } - Navigation.findNavController(rootView).popBackStack(fragmentId, false) + Navigation.findNavController(rootView).navigate(fragmentId) } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { diff --git a/app/src/main/java/org/fossasia/openevent/general/search/SearchResultsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/search/SearchResultsFragment.kt index e51d2f552..3d9a8bc93 100644 --- a/app/src/main/java/org/fossasia/openevent/general/search/SearchResultsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/search/SearchResultsFragment.kt @@ -31,10 +31,8 @@ import org.fossasia.openevent.general.common.EventClickListener import org.fossasia.openevent.general.common.FavoriteFabClickListener import org.fossasia.openevent.general.di.Scopes import org.fossasia.openevent.general.event.Event -import org.fossasia.openevent.general.event.EventDetailsFragmentArgs import org.fossasia.openevent.general.event.types.EventType import org.fossasia.openevent.general.favorite.FavoriteEventsRecyclerAdapter -import org.fossasia.openevent.general.utils.Utils.getAnimFade import org.fossasia.openevent.general.utils.Utils.setToolbar import org.fossasia.openevent.general.utils.extensions.nonNull import org.jetbrains.anko.design.longSnackbar @@ -44,7 +42,6 @@ import org.koin.androidx.scope.ext.android.getOrCreateScope import org.koin.androidx.viewmodel.ext.android.viewModel import timber.log.Timber import androidx.appcompat.view.ContextThemeWrapper -import androidx.navigation.fragment.findNavController class SearchResultsFragment : Fragment(), CompoundButton.OnCheckedChangeListener { @@ -194,11 +191,8 @@ class SearchResultsFragment : Fragment(), CompoundButton.OnCheckedChangeListener super.onViewCreated(view, savedInstanceState) val eventClickListener: EventClickListener = object : EventClickListener { override fun onClick(eventID: Long) { - EventDetailsFragmentArgs(eventID) - .toBundle() - .also { bundle -> - Navigation.findNavController(view).navigate(R.id.eventDetailsFragment, bundle, getAnimFade()) - } + Navigation.findNavController(view) + .navigate(SearchResultsFragmentDirections.actionSearchResultsToEventDetail(eventId = eventID)) } } val favFabClickListener: FavoriteFabClickListener = object : FavoriteFabClickListener { @@ -243,18 +237,14 @@ class SearchResultsFragment : Fragment(), CompoundButton.OnCheckedChangeListener true } R.id.filter -> { - SearchFilterFragmentArgs( + Navigation.findNavController(rootView) + .navigate(SearchResultsFragmentDirections.actionSearchResultsToSearchFilter( date = safeArgs.date, freeEvents = safeArgs.freeEvents, location = safeArgs.location, type = safeArgs.type, query = safeArgs.query, - sort = safeArgs.sort - ) - .toBundle() - .also { - Navigation.findNavController(rootView).navigate(R.id.searchFilterFragment, it, getAnimFade()) - } + sort = safeArgs.sort)) true } else -> super.onOptionsItemSelected(item) diff --git a/app/src/main/java/org/fossasia/openevent/general/search/SearchTimeFragment.kt b/app/src/main/java/org/fossasia/openevent/general/search/SearchTimeFragment.kt index 91b1bcff7..82b9a0ae1 100644 --- a/app/src/main/java/org/fossasia/openevent/general/search/SearchTimeFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/search/SearchTimeFragment.kt @@ -7,8 +7,7 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import androidx.navigation.NavOptions -import androidx.navigation.Navigation +import androidx.navigation.Navigation.findNavController import androidx.navigation.fragment.navArgs import kotlinx.android.synthetic.main.fragment_search_time.view.anytimeTextView import kotlinx.android.synthetic.main.fragment_search_time.view.todayTextView @@ -82,12 +81,10 @@ class SearchTimeFragment : Fragment() { private fun redirectToCaller(time: String) { searchTimeViewModel.saveTime(time) - val (destFragId, popUpId) = if (safeArgs.fromFragmentName == SEARCH_FILTER_FRAGMENT) - R.id.searchFilterFragment to R.id.searchResultsFragment + val destFragId = if (safeArgs.fromFragmentName == SEARCH_FILTER_FRAGMENT) + R.id.action_search_time_to_search_filter else - R.id.searchFragment to R.id.eventsFragment - - val navOptions = NavOptions.Builder().setPopUpTo(popUpId, false).build() + R.id.action_search_time_to_search val navArgs = if (safeArgs.fromFragmentName == SEARCH_FILTER_FRAGMENT) { SearchFilterFragmentArgs( @@ -95,7 +92,7 @@ class SearchTimeFragment : Fragment() { ).toBundle() } else null - Navigation.findNavController(rootView).navigate(destFragId, navArgs, navOptions) + findNavController(rootView).navigate(destFragId, navArgs) } override fun onOptionsItemSelected(item: MenuItem): Boolean { diff --git a/app/src/main/java/org/fossasia/openevent/general/search/SearchTypeFragment.kt b/app/src/main/java/org/fossasia/openevent/general/search/SearchTypeFragment.kt index cec4a633b..33bdea151 100644 --- a/app/src/main/java/org/fossasia/openevent/general/search/SearchTypeFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/search/SearchTypeFragment.kt @@ -8,8 +8,7 @@ import android.view.ViewGroup import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.lifecycle.Observer -import androidx.navigation.NavOptions -import androidx.navigation.Navigation +import androidx.navigation.Navigation.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager import kotlinx.android.synthetic.main.content_no_internet.view.retry @@ -101,20 +100,18 @@ class SearchTypeFragment : Fragment() { private fun redirectToSearch(type: String) { searchTypeViewModel.saveType(type) - val (destFragId, popUpId) = if (safeArgs.fromFragmentName == SEARCH_FILTER_FRAGMENT) - R.id.searchFilterFragment to R.id.searchResultsFragment + val destFragId = if (safeArgs.fromFragmentName == SEARCH_FILTER_FRAGMENT) + R.id.action_search_type_to_search_filter else - R.id.searchFragment to R.id.eventsFragment + R.id.action_search_type_to_search - val navOptions = NavOptions.Builder().setPopUpTo(popUpId, false).build() - Navigation.findNavController(rootView).navigate(destFragId, null, navOptions) val navArgs = if (safeArgs.fromFragmentName == SEARCH_FILTER_FRAGMENT) { SearchFilterFragmentArgs( query = safeArgs.query ).toBundle() } else null - Navigation.findNavController(rootView).navigate(destFragId, navArgs, navOptions) + findNavController(rootView).navigate(destFragId, navArgs) } private fun setCurrentChoice(value: String?) { diff --git a/app/src/main/java/org/fossasia/openevent/general/ticket/TicketsFragment.kt b/app/src/main/java/org/fossasia/openevent/general/ticket/TicketsFragment.kt index ae897fb65..0e7706026 100644 --- a/app/src/main/java/org/fossasia/openevent/general/ticket/TicketsFragment.kt +++ b/app/src/main/java/org/fossasia/openevent/general/ticket/TicketsFragment.kt @@ -26,12 +26,8 @@ import kotlinx.android.synthetic.main.fragment_tickets.view.ticketTableHeader import kotlinx.android.synthetic.main.fragment_tickets.view.ticketsRecycler import kotlinx.android.synthetic.main.fragment_tickets.view.time import org.fossasia.openevent.general.R -import org.fossasia.openevent.general.attendees.AttendeeFragmentArgs -import org.fossasia.openevent.general.auth.LoginFragmentArgs import org.fossasia.openevent.general.event.Event import org.fossasia.openevent.general.event.EventUtils -import org.fossasia.openevent.general.utils.Utils.getAnimFade -import org.fossasia.openevent.general.utils.Utils.getAnimSlide import org.fossasia.openevent.general.utils.extensions.nonNull import org.fossasia.openevent.general.utils.nullToEmpty import org.koin.androidx.viewmodel.ext.android.viewModel @@ -147,20 +143,15 @@ class TicketsFragment : Fragment() { private fun redirectToAttendee() { val wrappedTicketAndQty = TicketIdAndQtyWrapper(ticketIdAndQty) - - AttendeeFragmentArgs(eventId = safeArgs.eventId, ticketIdAndQty = wrappedTicketAndQty) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.attendeeFragment, bundle, getAnimSlide()) - } + findNavController(rootView).navigate(TicketsFragmentDirections.actionTicketsToAttendee( + eventId = safeArgs.eventId, ticketIdAndQty = wrappedTicketAndQty + )) } private fun redirectToLogin() { - LoginFragmentArgs(getString(R.string.log_in_first)) - .toBundle() - .also { bundle -> - findNavController(rootView).navigate(R.id.loginFragment, bundle, getAnimFade()) - } + findNavController(rootView).navigate(TicketsFragmentDirections.actionTicketsToLogin( + getString(R.string.log_in_first) + )) } private fun handleTicketSelect(id: Int, quantity: Int) { diff --git a/app/src/main/res/layout/fragment_search_location.xml b/app/src/main/res/layout/fragment_search_location.xml index f6572146d..b121c8164 100644 --- a/app/src/main/res/layout/fragment_search_location.xml +++ b/app/src/main/res/layout/fragment_search_location.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="@android:color/white" android:orientation="vertical"> + tools:layout="@layout/fragment_welcome" > + + + tools:layout="@layout/fragment_events" > + + + + + + + + + + + + + + + + + + + + + + + + + + + tools:layout="@layout/fragment_favorite" > + + + + + + + + + + + + + + + + + tools:layout="@layout/fragment_profile" > + + + + + + + + + + + + + + + + + + + + + + + + + + + +