From 58249b1efd8aa5ca1173f278cde6dab9212752d4 Mon Sep 17 00:00:00 2001 From: Pablo Guerra Date: Thu, 22 Aug 2024 20:55:43 -0400 Subject: [PATCH] Markdown Descriptions --- CHANGELOG.md | 4 + .../docs/professional_descriptions/am3_lab.md | 11 ++ .../docs/professional_descriptions/ambots.md | 15 ++ .../atlantic_insurance.md | 7 + .../project_descriptions/amg_gt3_wheel.md | 5 + .../android_calculator.md | 3 + .../apple_charging_station.md | 3 + assets/docs/project_descriptions/blokus.md | 41 +++++ .../project_descriptions/f1_sim_engineer.md | 3 + .../flutter_resume_builder.md | 31 ++++ assets/docs/project_descriptions/handroid.md | 3 + .../project_descriptions/led_wall_decor.md | 3 + assets/docs/project_descriptions/macro_pad.md | 3 + .../project_descriptions/mobile_sim_wheel.md | 23 +++ .../project_descriptions/project_lemans.md | 3 + .../project_descriptions/safedrive_mk1.md | 3 + .../project_descriptions/swe_632_lottery.md | 164 ++++++++++++++++++ .../project_descriptions/switch_speaker.md | 3 + .../xbox_head_tracking.md | 5 + assets/json/professional_experience.json | 9 +- assets/json/projects.json | 15 -- lib/models/professional_experience.dart | 22 +-- lib/models/project.dart | 18 +- lib/pages/details/details.controller.dart | 14 +- lib/pages/details/details.model.dart | 6 +- lib/pages/details/details.screen.dart | 12 +- lib/pages/details/widgets/markdown_view.dart | 57 ++++++ pubspec.lock | 48 +++++ pubspec.yaml | 6 +- 29 files changed, 487 insertions(+), 53 deletions(-) create mode 100644 assets/docs/professional_descriptions/am3_lab.md create mode 100644 assets/docs/professional_descriptions/ambots.md create mode 100644 assets/docs/professional_descriptions/atlantic_insurance.md create mode 100644 assets/docs/project_descriptions/amg_gt3_wheel.md create mode 100644 assets/docs/project_descriptions/android_calculator.md create mode 100644 assets/docs/project_descriptions/apple_charging_station.md create mode 100644 assets/docs/project_descriptions/blokus.md create mode 100644 assets/docs/project_descriptions/f1_sim_engineer.md create mode 100644 assets/docs/project_descriptions/flutter_resume_builder.md create mode 100644 assets/docs/project_descriptions/handroid.md create mode 100644 assets/docs/project_descriptions/led_wall_decor.md create mode 100644 assets/docs/project_descriptions/macro_pad.md create mode 100644 assets/docs/project_descriptions/mobile_sim_wheel.md create mode 100644 assets/docs/project_descriptions/project_lemans.md create mode 100644 assets/docs/project_descriptions/safedrive_mk1.md create mode 100644 assets/docs/project_descriptions/swe_632_lottery.md create mode 100644 assets/docs/project_descriptions/switch_speaker.md create mode 100644 assets/docs/project_descriptions/xbox_head_tracking.md create mode 100644 lib/pages/details/widgets/markdown_view.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aa0d10..ffe61ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.0 + +- NEW: The `Details` page now supports Markdown rendering for the body text. + ## 2.7.0 - NEW: Added a filter menu to the Personal Projects section. diff --git a/assets/docs/professional_descriptions/am3_lab.md b/assets/docs/professional_descriptions/am3_lab.md new file mode 100644 index 0000000..6ce45b4 --- /dev/null +++ b/assets/docs/professional_descriptions/am3_lab.md @@ -0,0 +1,11 @@ +As a Graduate Research Assistant at the AM³ Lab, University of Arkansas, I played a pivotal role in advancing the cutting-edge field of 3D printing technology. The AM³ Lab is dedicated to developing next generation 3D printing technologies and promoting the use of 3D printing technology to better our lives through excellent research, education, and service to the global 3D printing community. + +Initially on-boarded as a volunteer, I assisted in troubleshooting and enhancing the 5th-Generation 3D printers. My dedication and problem-solving skills quickly earned me the position of Graduate Research Assistant, where I continued my contributions to the field. + +My primary focus during my time with the lab was developing a groundbreaking swarm 3D printing process designed to enable the production of large-scale structures using a fleet of mobile robots. This innovative approach involved a combination of hardware and software components, including custom-built transporter robots, SCARA 3D printers, and a modular print & power-delivery floor. + +Most notably, I was instrumental in transitioning from the 5th-Generation platform to the 6th and 7th-Generations. This transition involved separating the SCARA arm from the mobile base, and optimizing the hardware for improved robustness, efficiency, and quality. I assumed a significant role in these evolutions, taking charge of software development and contributing to hardware enhancements. + +One of my key contributions was the implementation of Computer Vision for localization and navigation within the transporter's control system, enabling semi-autonomous operation - a significant advancement in the field. + +Not only was my work at the AM³ Lab a valuable learning experience, but also a contribution to the lab's mission of pushing the boundaries of 3D printing technology. I am proud to have been part of a team dedicated to revolutionizing manufacturing and enabling mass customization through next-generation 3D printing innovations. diff --git a/assets/docs/professional_descriptions/ambots.md b/assets/docs/professional_descriptions/ambots.md new file mode 100644 index 0000000..4ef21af --- /dev/null +++ b/assets/docs/professional_descriptions/ambots.md @@ -0,0 +1,15 @@ +As the Lead Robotics Engineer at AMBOTS Inc., I had the privilege of spearheading the transformation of groundbreaking research from the AM³ Lab at the University of Arkansas into a game-changing commercial product. AMBOTS Inc. is dedicated to revolutionizing the future of manufacturing through its patent-pending swarm 3D printing and assembly technologies, which were originally conceived by Dr. Wenchao Zhou in 2015. + +Our vision at AMBOTS was nothing short of a manufacturing revolution - a world where production is fully digitized and automated, allowing for the creation of customized products on demand, all at a remarkably low cost. Similar to how a swarm of bees collaborates to build its nest, our smart and autonomous mobile robots were engineered to work seamlessly together, translating digital designs into tangible products. + +The team and I achieved some remarkable milestones some of which include: + +- Our groundbreaking work earned us recognition within the industry, including winning the prestigious "3D Startup of the Year" by 3D Natives. We were also featured on GE Reports as one of "The 5 Coolest Things On Earth This Week" and received coverage on CBS, including an appearance on "The Henry Ford's Innovation Nation." + +- We filed patent applications to protect our swarm 3D printing and assembly technology, solidifying our position as pioneers in the field. + +- Our dedication and innovation secured Phase I and Phase II of the SBIR grant, totaling $1,225,000 in funding, which fueled our continued growth and development. + +- We landed our company's first customer, validating the real-world applicability of our technology. + +My time as the Lead Robotics Engineer at AMBOTS Inc. was marked by a relentless dedication to pushing the boundaries of what's possible in advanced manufacturing. I am proud to have played a pivotal role in bringing the vision of fully automated, on-demand, and cost-effective digital manufacturing closer to reality. Our journey continues, and I look forward to witnessing where the future of this transformative technology takes us. diff --git a/assets/docs/professional_descriptions/atlantic_insurance.md b/assets/docs/professional_descriptions/atlantic_insurance.md new file mode 100644 index 0000000..62fe49b --- /dev/null +++ b/assets/docs/professional_descriptions/atlantic_insurance.md @@ -0,0 +1,7 @@ +As a contract software engineer, I led the strategic planning, development, and successful launch of the AIC Mobile App and Management Portal for Atlantic Insurance Company Limited. + +This innovative app, available for Android and iOS, was meticulously designed with a customer-centric approach to enrich interactions with the company. + +Its features includes access to insured item details, a digital certificate of insurance, timely renewal notifications, claims filing, quote requests, office location guidance, and more. + +This endeavor reflects AIC's commitment to delivering unsurpassed customer service, upholding integrity, demonstrating professionalism, and fostering teamwork in line with Atlantic Insurance Company Limited's values. diff --git a/assets/docs/project_descriptions/amg_gt3_wheel.md b/assets/docs/project_descriptions/amg_gt3_wheel.md new file mode 100644 index 0000000..323ed07 --- /dev/null +++ b/assets/docs/project_descriptions/amg_gt3_wheel.md @@ -0,0 +1,5 @@ +The goal of the AMG GT3 Wheel project was to design and build a functional, 3D-printed replica of the Mercedes Benz AMG GT3 steering wheel for use in Sim Racing. + +An Arduino Nano powers the wheel and features a thumb-slide joystick, eight pushbuttons, and a pair of paddle shifters. The wheel is directly compatible with the Thrustmaster ecosystem. + +You can find the 3D-printable files on Thingiverse along with an assembly manual, circuit files, and Arduino code. diff --git a/assets/docs/project_descriptions/android_calculator.md b/assets/docs/project_descriptions/android_calculator.md new file mode 100644 index 0000000..7edac1e --- /dev/null +++ b/assets/docs/project_descriptions/android_calculator.md @@ -0,0 +1,3 @@ +Android Calculator is a calculator app developed for a mobile programming class assignment at the University of Arkansas. + +The app was designed in Photoshop using popular calculator apps for design reference, then programmed in Java using Android Studio. diff --git a/assets/docs/project_descriptions/apple_charging_station.md b/assets/docs/project_descriptions/apple_charging_station.md new file mode 100644 index 0000000..4ddfdc6 --- /dev/null +++ b/assets/docs/project_descriptions/apple_charging_station.md @@ -0,0 +1,3 @@ +The Apple Charging Station, initially sketched in 2016, was later revived and given as a gift to a friend. + +Redesigned in Autodesk Fusion 360, printed using wood-infused PLA, and finished with a wood stain. Supports charging of an iPhone, Apple Watch, and the original AirPods. The charging modules are interchangeable to support different product dimensions. diff --git a/assets/docs/project_descriptions/blokus.md b/assets/docs/project_descriptions/blokus.md new file mode 100644 index 0000000..f435a47 --- /dev/null +++ b/assets/docs/project_descriptions/blokus.md @@ -0,0 +1,41 @@ +The real-time multiplayer Blokus game, developed by Charles Tucker and Pablo L. Guerra, was designed using Flutter and Supabase as part of the final project for GMU’s ISA 681 ([Secure Software Design and Programming](https://dwheeler.com/secure-class/)) class. The game provides an engaging multiplayer experience, leveraging Supabase as the backend and Flame as the game engine. + +The project requirements were as follows: + +> You are part of a small development shop that has been asked to develop a network-enabled game system. The games will be played between competitors that do not trust each other and may try to cheat. + +> Your software must actively prevent players from cheating, that is, from exploiting the program to win or to force other players to lose. Act as if players who win will earn money, while players who lose will lose money; thus, the system must know which players win or lose, and must enforce the game rules even if the players are actively trying to circumvent the rules. Note that a system where all competing users log in as a single account cannot possibly meet this requirement. The software must support at least 50 simultaneous players (in most cases this implies there will be a large number of simultaneous games). + +> The program must be designed to be secure. You must avoid vulnerabilities like SQL injection, buffer overflows, accepting invalid input, and passing sensitive data unprotected. You will also need to authenticate all users. You do not need to prevent out-of-game collusion. + +## Materials and Methods + +The game was developed using the Flutter framework, written in the Dart programming language. The Supabase platform was used to provide real-time functionality for multiplayer gameplay, while the Flame game engine was used to implement the game mechanics and rendering. + +User authentication was handled using the Supabase Magic Link feature, which allows users to sign in by clicking a link sent to their email address. The link is valid for a limited time and contains a one-time password, ensuring secure authentication. + +Real-time communication between players was facilitated by Supabase’s 'Realtime' feature, which allows clients to subscribe to changes in the database and receive updates in real-time. This feature was used to synchronize game state between players, ensuring all players in a game room see the same board and pieces, without impacting performance. + +Turn validation is handled based on the game rules, ensuring only the player with the given ID and color can place a piece on the board. The game logic enforces the rules of Blokus, such as piece placement, rotation, and adjacency restrictions. + +A session timeout feature was implemented to handle cases where a player disconnects or leaves the game. If a player does not make a move within ten minutes, they are automatically forfeited, and the game continues without them. + +Upon finishing the game, a score screen is displayed showing the final scores and the order in which pieces were placed. + +## Results + +The development of the real-time multiplayer Blokus game met several key project requirements, but also revealed some limitations. The primary outcomes include: + +1. Real-time Multiplayer Gameplay: Supabase’s ‘Realtime’ feature successfully synchronized the game state across multiple players, ensuring that all participants in a game room experienced the same board updates simultaneously. This demonstrated the effectiveness of using Supabase for real-time communication in a multiplayer gaming environment. +2. User Authentication and Security: The implementation of Supabase’s Magic Link authentication provided a secure login method, preventing unauthorized access. This met the project’s security requirements by ensuring that only authenticated users could participate in the game. +3. Turn Validation and Rule Enforcement: The game logic enforced Blokus rules, restricting piece placement and ensuring only the correct player could take their turn. This reduced the likelihood of cheating and maintained the integrity of the game. +4. Session Timeout Feature: The inclusion of a session timeout mechanism automatically forfeited inactive players, ensuring that the game could continue without disruption. This feature maintained the flow of gameplay even if a player disconnected or became inactive. +5. Unresolved Issues: Despite these successes, the game had some unresolved issues. The score calculation logic was inconsistent, sometimes failing to correctly tally the final scores. Additionally, the endgame logic was incomplete, occasionally leading to games that could not properly conclude. The game also initially implemented a peer-to-peer architecture for turn validation, which did not align with the professor’s preference for a server-client model. + +## Discussion + +This project effectively demonstrated the capabilities of Flutter, Supabase, and Flame in building a secure, real-time multiplayer game. While core functionalities were successfully implemented, the issues with score calculation and endgame logic highlight the need for further refinement. The initial peer-to-peer approach for turn validation, although functional, did not meet the optimal security standards offered by a server-client model. Addressing these issues in future iterations will be crucial for enhancing the game’s reliability and scalability. + +## Conclusion + +The real-time multiplayer Blokus game prototype showcased the potential of modern web technologies in developing secure multiplayer experiences. Despite challenges like inconsistent scoring and incomplete game logic, the project provides a strong foundation for future development. Transitioning to a server-client architecture and resolving existing issues will be key to building a more robust and scalable multiplayer game. diff --git a/assets/docs/project_descriptions/f1_sim_engineer.md b/assets/docs/project_descriptions/f1_sim_engineer.md new file mode 100644 index 0000000..5d50083 --- /dev/null +++ b/assets/docs/project_descriptions/f1_sim_engineer.md @@ -0,0 +1,3 @@ +F1 Sim Engineer, co-developed with Viktor Soendergaard for a mobile programming class at the University of Arkansas, is a companion app for the F1 2019 sim racing game. + +The app is written in Java and uses the UDP protocol to communicate with the gaming device to receive and display live telemetry data sent from the F1 2019 game. The app obtains various data from the game, including event information, race details, number of laps, number of participants, weather conditions, vehicle setup, vehicle status, and telemetry. This information is displayed live during the race is saved to the local database for later review. diff --git a/assets/docs/project_descriptions/flutter_resume_builder.md b/assets/docs/project_descriptions/flutter_resume_builder.md new file mode 100644 index 0000000..e73a844 --- /dev/null +++ b/assets/docs/project_descriptions/flutter_resume_builder.md @@ -0,0 +1,31 @@ +The Flutter Resume Builder is an open-source, user-friendly progressive web application designed to simplify the creation of professional resumes. Key features include resume creation, editing, downloading in PDF and JSON formats, importing existing resumes, direct printing, and optional image integration. These functionalities make it an intuitive and versatile tool for job seekers and developers, offering a seamless platform for crafting polished and impactful resumes. + +## Materials and Methods + +Developed using the Flutter framework and Dart programming language, the Flutter Resume Builder is designed as a Progressive Web App (PWA) to ensure broad compatibility and accessibility. + +**Key Features:** + +- **Create and Edit Resumes:** Users can input personal details, work experience, education, and skills, with real-time editing and immediate visual feedback. +- **Download and Print:** Resumes are downloadable in both PDF and JSON formats, with a print feature for producing hard copies directly from the browser. +- **Import Resumes:** Existing resumes in JSON format can be imported for quick updates, simplifying the maintenance of professional documents. +- **Optional Image Integration:** Users can add an image such as a QR code, logo, or headshot to their resume. + +The app’s responsive design ensures smooth operation on desktops, tablets, and smartphones. Testing across various browsers and devices was conducted to guarantee a consistent and reliable user experience. + +## Results + +The Flutter Resume Builder delivers a comprehensive web application with the following outcomes: + +- **Ease of Use:** An intuitive interface streamlines resume creation and customization with real-time previews. +- **Flexible Export Options:** Users can download resumes in PDF for sharing or printing, and in JSON for easy future editing. The print feature simplifies producing physical copies. +- **Cross-Platform Compatibility:** The PWA design ensures consistent performance across devices and platforms, whether on desktop or mobile. +- **Efficient Resume Management:** The import feature allows easy updates to existing resumes, supporting ongoing professional development. + +## Discussion + +This project demonstrates how Flutter can be used to create a cross-platform PWA that effectively meets users’ needs for resume creation and management. Features like multi-format downloads, printing, and real-time editing ensure a robust user experience. Future enhancements could include additional resume templates, advanced customization options, spell-check, grammar suggestions, and cloud storage for better document management. + +## Conclusion + +The Flutter Resume Builder is a powerful and accessible tool for creating, editing, and managing professional resumes. Leveraging the strengths of Flutter and PWAs, it provides a reliable and efficient solution for job seekers and developers. While there is room for further enhancements, the current version is a solid foundation for users looking to streamline their resume-building process. diff --git a/assets/docs/project_descriptions/handroid.md b/assets/docs/project_descriptions/handroid.md new file mode 100644 index 0000000..e511b1a --- /dev/null +++ b/assets/docs/project_descriptions/handroid.md @@ -0,0 +1,3 @@ +The Handroid project was a science fair submission for my junior year of high school. The idea was inspired by arms developed in medical fields, where mechanical arms can mimic finger movements. + +The shape of the arm was made using foam, cardboard, pieces of wood, and fiberglass. Rubber tubes are used for the fingers with incisions made at the bending points. The fingertips are each connected to a fishing line, which routes internally through the finger and arm, then attaches to the horn of a servo motor. An Arduino microcontroller controls the servo motors and receives input from a modified glove with flex sensors attached to each finger. When the input glove is worn, the flex sensors record the amount of bending of each finger which the Arduino uses to determine the degree of rotation for each servo motor to replicate the users' finger movements. diff --git a/assets/docs/project_descriptions/led_wall_decor.md b/assets/docs/project_descriptions/led_wall_decor.md new file mode 100644 index 0000000..36b1d7f --- /dev/null +++ b/assets/docs/project_descriptions/led_wall_decor.md @@ -0,0 +1,3 @@ +The LED wall decor was made as an art piece for a friend. The signs' colors and patterns are controllable using the Blynk app. + +It features an Arduino MKR1000 controlling eight RGB LEDs and connects to the internet using its onboard WiFi module. The LED cover plate is interchangeable using magnets. A USB micro cable is used for continuous power, but the device can also be powered using a 3.7V LiPo battery and charged with the MKR's onboard charging circuit. One of the LEDs was not working at the time the photos were taken. diff --git a/assets/docs/project_descriptions/macro_pad.md b/assets/docs/project_descriptions/macro_pad.md new file mode 100644 index 0000000..af4355f --- /dev/null +++ b/assets/docs/project_descriptions/macro_pad.md @@ -0,0 +1,3 @@ +Macro Pad is a programmable interface used for executing custom macros (button combinations or input sequences). It allows for extended control and execution of complex commands with a single button press. + +The device features an Arduino Micro, 16 Cherry MX switches, a rotatable and pressable knob, and a micro USB port for connecting. diff --git a/assets/docs/project_descriptions/mobile_sim_wheel.md b/assets/docs/project_descriptions/mobile_sim_wheel.md new file mode 100644 index 0000000..811872d --- /dev/null +++ b/assets/docs/project_descriptions/mobile_sim_wheel.md @@ -0,0 +1,23 @@ +The Mobile Sim Wheel Kit is a fully 3D-printable steering wheel designed for mobile sim racing. This project is based on one of my earlier and very successful projects, the [AMG GT3 Wheel Kit for the Thrustmaster ecosystem](http://localhost:57724/#/home/personal-projects/details/amg-gt3-wheel). The wheel's compact dimensions (230mm x 130mm) and integrated clamp make it ideal for mobile use, offering a satisfying racing experience with games that support tilt-to-steer. + +## Materials and Methods + +The previously created SVG file for the AMG GT3 wheel was modified in Adobe Illustrator to remove unnecessary details and make the design more mobile-friendly. The altered SVG file was loaded into Fusion 360 and scaled to the desirable dimensions (230 x 130 mm). The wheel was then enhanced, integrating grip handles and slots for attaching a mobile phone mount. The phone mount was adapted from a [Thingiverse design](https://www.thingiverse.com/thing:2194278) and tailored to the wheel's dimensions. + +The final model was exported as STL files and then sliced with Cura. The wheel was printed with PLA filament in less than 15 hours on an Ender 3. + +The components were assembled in under 30 minutes using glue and snap-fit connections. + +The wheel's design was iterated following testing. The final version includes 'fidget' buttons with a TPU-based spring mechanism, a reworked handle design that closely resembles the actual AMG GT3 wheel, thumb cutouts on the phone mount for easier access to the screen's edges, and improved tolerances for the snap-fit connections. + +## Results + +The final Mobile Sim Wheel kit achieved its design objectives. The reduced dimensions made it easier to utilize mobile devices, while the built-in handles and improved phone mount ensured secure and comfortable use during gameplay. The inclusion of 'fidget' buttons and a modified grip design added to a more realistic appearance. + +## Discussion + +The Mobile Sim Wheel kit's design process demonstrated the value of testing and iteration in creating a functional and user-friendly product. The use of 3D-printable spring mechanisms for the 'fidget' buttons removed the need for extra hardware. However, even after evaluating several designs and materials, it was difficult to provide the desired tactile feedback and durability. This problem would be best solved with off-the-shelf springs for improved feedback and longevity. The phone mount's cutouts improved the overall user experience on iOS devices allowing interaction with the home bar in landscape mode. + +## Conclusion + +The Mobile Sim Wheel kit provides a simple and practical solution for mobile sim racing enthusiasts. The kit uses 3D printing technology to provide a cost-effective and customized solution that is quick and easy to assemble. The project showcases 3D printing's ability to create useful, high-quality goods adapted to unique objectives. The design and testing phases revealed areas for improvement, such as the use of genuine springs for the 'fidget' buttons. Overall, the Mobile Sim Wheel kit offers an immersive and enjoyable racing experience on mobile devices, which enhances gameplay for sim racers. diff --git a/assets/docs/project_descriptions/project_lemans.md b/assets/docs/project_descriptions/project_lemans.md new file mode 100644 index 0000000..61be17a --- /dev/null +++ b/assets/docs/project_descriptions/project_lemans.md @@ -0,0 +1,3 @@ +The goal of Project LeMans was to build a relatively low-budget sim racing rig out of easy-to-access parts and materials. It needed to have the basic functionality of a sim racing wheel and pedal set. + +It features a Teensy for the brains, emulating a gaming controller and supporting input from the steering wheel's rotation, its two buttons, pair of paddle shifters, as well as the throttle, brakes, and clutch located on the pedal base. diff --git a/assets/docs/project_descriptions/safedrive_mk1.md b/assets/docs/project_descriptions/safedrive_mk1.md new file mode 100644 index 0000000..6b0baaa --- /dev/null +++ b/assets/docs/project_descriptions/safedrive_mk1.md @@ -0,0 +1,3 @@ +The goal of the SafeDrive MK1 project was to design a secure storage device that unlocked using biometric authentication and is capable of self-destructing. + +It features an Arduino micro-controller, an RGB backlight LCD Panel, a TTL fingerprint scanner, an interchangeable USB flash drive slot, and a USBKill device. If the device recognizes a fingerprint, it will provide access to the internal USB flash drive. However, after ten failed attempts, the Arduino enables the USBKill device, releasing close to 200 negative volts (in the reverse direction), destroying the internal drive's data lines and any unprotected connected devices. diff --git a/assets/docs/project_descriptions/swe_632_lottery.md b/assets/docs/project_descriptions/swe_632_lottery.md new file mode 100644 index 0000000..ccb12ec --- /dev/null +++ b/assets/docs/project_descriptions/swe_632_lottery.md @@ -0,0 +1,164 @@ +The SWE 632 Lottery Web App was developed as part of the [User Interface Design and Development course](https://cs.gmu.edu/~tlatoza/teaching/swe632f21/home.html) at George Mason University (GMU). This project, created in collaboration with Afrina Sharmin and Suzzana Rafi, offers an immersive gaming experience that includes Wheel of Fortune and Scratch Cards, allowing users to earn virtual currency to unlock GMU-branded merchandise. The application was built using Flutter and Dart, leveraging the capabilities of Progressive Web Apps (PWAs) to provide a seamless user experience across various devices. The app is fully functional and hosted on GitHub Page. + +## Materials and Methods + +The app was written in Dart using the Flutter framework, and followed an iterative development process based on the lectures and assignments from the course. Each submission was published on GitHub Pages to allow for easy access and testing by peers and the instructor. + +#### Submission 1 [(v0.0.2)](https://plguerradesigns.github.io/swe_632_lottery/v0.0.2/) included the following features: + +1. A light and dark theme based on the GMU color palette and brand assets +2. A frosted glass effect surrounding the main content area on each screen +3. A home screen with the ability to change themes, navigate to the games, and view the list of rewards +4. A Wheel of Fortune game allowing users to press a button to spin the wheel and potentially win a reward +5. A Scratch Cards game allowing users to scratch off a grid, revealing a reward or empty space, and winning if they uncover three of the same reward +6. A rewards screen displaying the list of available rewards and whether they have been unlocked +7. Pop-up dialogs to display the results of the games and game instructions + +This submission was critiqued by peers, who provided a heuristic evaluation of the app's usability and design. The following feedback was selected to be addressed in the subsequent submission. + +> **Match between the system and the real world:** Users may become confused when they hover over interactive elements, such as icons, without any tooltip or other explanation of what it does. This delay in response might make navigating less natural and less enjoyable for users as a whole. + +> **Help and Documentation**: Users are left without instructions or resources to browse and comprehend the functionalities of the website due to the lack of support and documentation on the homepage. The "Help and documentation" heuristic is broken by this omission, which could lead to confusion and lessen user satisfaction + +> **User Control and Freedom**: There is no cancel or back option when a game ends. We can only select to play again or view rewards. + +> **Error Prevention**: There is an unusual output while playing the scratch card game. The player is getting rewards without scratching all the blocks in the scratch cards. + +> **Consistency and Standards**: The probability of winning in scratch cards is very low, approximately 0. + +> **Match between system and the real world**: The heuristic is broken by the wheel of fortune’s use of “?” symbols, which leaves users in the dark about possible prizes. Because players in classic Wheel of Fortune games are aware of the potential results, there is transparency and a clear understanding of what to expect. + +#### Submission 2 [(v0.0.4)](https://plguerradesigns.github.io/swe_632_lottery/v0.0.4/) introduced the following changes based on the feedback: + +- Icon buttons were replaced with text buttons to improve clarity and user understanding. +- A Help button was added to the home screen, which opens a dialog with instructions on how to play the games. +- A 'Go Back' button was added to the results dialog, allowing users to return to the game screen after viewing their results. +- The Scratch Cards game was updated to prevent rewards from being revealed until all blocks are scratched off. +- The probability of winning in the Scratch Cards game was increased to 50% to improve user satisfaction. +- The Wheel of Fortune game was updated to display the potential rewards instead of question marks, providing transparency to users. +- The Wheel of Fortune's color scheme was updated to match the GMU brand colors, enhancing visual consistency. +- Supporting content was moved into the Frosted containers to improve the visual hierarchy and user experience. +- The initial theme was set to the user's system preference. + +This submission was then evaluated through a 'Think Aloud' usability test, where participants were asked to complete specific tasks while verbalizing their thoughts. The test details and results are as follows: + +> **Task Description** In this user study, participants will engage with the SWE 632 Lottery web app, which offers two primary use cases: Spin the Wheel and Scratch Cards. Participants will be tasked with simulating the user experience by unlocking ten different GMU merchandise rewards. To achieve this, they must successfully unlock five rewards by interacting with the Spin the Wheel feature and five more through Scratch Cards. The study will assess the user-friendliness, engagement, and overall effectiveness of these features in providing an enjoyable and rewarding experience for the participants while gauging the app's appeal and functionality in promoting GMU merchandise. + +> > **Participant A** +> +> > **Critical Incidents:** +> +> 1. User was unable scroll through the reward list using gestures or arrow keys. The user was able to overcome this by dragging the scrollbar. +> 2. The user had issues figuring out how to scratch the card. The UI took some time to register and render the users' inputs while scratching the card. +> 3. The application froze for about 5-8 seconds after the user attempted to exit the rewards screen. +> 4. The user had to constantly move between the game page and rewards page to identify the number of rewards won. +> 5. The user had to manually count the number of unlocked rewards to determine if the task was completed. +> +> > **Post-Task Interview Notes:** +> +> 1. The user expected the wheel of fortune game to have empty spaces where they would not win any prizes. +> 2. The user initially assumed that the scratch card game required three items in a row instead of three items anywhere on the card. + +> > **Participant B** +> +> > **Critical Incidents:** +> +> 1. The user played spin the wheel game first. The wheel initially showed the user that he won a cap, but in the popup, it displayed a mug image. After a while, it changed back to showing a cap automatically. +> 2. The user went to the reward view section from the popup, and it showed two rewards: a mug and a cap, even though the user only played the game once. +> 3. The user had difficulty navigating back to another game from the reward and game pages. He had to switch reward and game page frequently. +> 4. The user got confused about how to play the scratch card game but eventually found instructions on how to play. +> 5. After finishing the first round of the scratch card game, the user went to the reward page. When he returned to the scratch card game, he expected to see a new game page, but he found the old page instead. To access a new game page, the user needed to go back to the home page repeatedly. +> 6. The user attempted to fix the issue by refreshing the browser, but it loaded the home page, where the user expected to find the scratch card for a new game. +> 7. The user discovered that all the rewards were gone after refreshing the browser. +> +> > **Post-Task Interview Notes:** +> +> 1. The user thought there was an issue with the spin the wheel game when he won a cap. +> 2. The user expects to see a cross sign in the top right corner to navigate back from the reward page. +> 3. The scratch card game doesn't automatically load a new game page in both cases, whether the user wins or loses the game. +> 4. While playing the spin the wheel game, the user mentioned that the wheel spins very quickly, which caused discomfort to his eyes. + +> > **Participant C** +> +> > **Critical incidents:** +> +> 1. For spin the wheel game, the user clicked on the “spin” button multiple times while the wheel was still spinning from the first click, and had multiple rewards pop up one after another after it was done spinning. +> 2. For the scratch card game, when the user won a reward, he selected to view the reward and selected “go back”, the page did not reset to the original state, he had to click the back button and start the game again to get a new game page. +> 3. The user was confused after scratching three cells as he was expecting the game to end there. +> 4. The user did not know how much he had to scratch for it to be accepted. He first scratched each cell a little and when he was done with all cells nothing popped up. He had to eventually scratch more for it to pop up the notification. +> +> > **Post-Task interview notes:** +> +> 1. For spin the wheel game, the user won 5 unique rewards after 5 tries, there were no repetition of rewards. Getting the same reward twice would have made it more realistic. +> 2. The user won a prize every time in the spin the wheel game, there were no options like “bad luck, try again” or something similar. That was also not expected by the user. +> 3. The user had to scratch all the cells to get three matching rewards, which was tiring to do. He would have preferred scratching three times and having the game ending there. +> 4. After scratching 8 out of the 9 cells, he did not see any point in having to waste time scratching the last cell as none of the other cells had any two matching rewards, he thought the game could have ended by then. + +> > **Participant D** +> +> > **Critical incidents:** +> +> 1. The user initially thought they could only scratch a max of three sections on the scratch card game and was confused when nothing happened. The user then realized that they had to scratch the entire card to end the game. +> 2. The user was unable to reset the scratched scratch card after clicking outside the end game dialog box. The user was able to fix this by navigating to the home screen and back to the scratch card game. +> 3. The user was able to click the spin the wheel button multiple times and successfully won several items in rapid succession, instead of one at a time. +> +> > **Post-Task interview notes:** +> +> 1. The user had no additional comments after completing the task. + +> > **Usability Issues** +> +> 1. **B5, C2:** The scratch card game does not automatically load a new game page after going to the reward page and coming back to the game page. When the user wishes to play the game again, the page remains in its previous state, and this presents a significant usability problem that needs to be addressed. +> 2. **A2, C3:** The user was not sure how much to scratch the card to make actions happen, and the UI responded slowly to their scratching. They expected the game would end after scratching three cells, but it didn't provide them with any feedback, leaving them confused as to what was going on. The user became frustrated due to the absence of timely response and clear instructions. +> 3. **A1, B3:** Another usability issue is the need for the user to frequently switch between the game page and the rewards page in order to keep track of the number of rewards they have won. This constant back-and-forth navigation made it challenging for the user to easily return to their game from either the reward or game pages, causing a disruption in the overall gaming experience. +> 4. **C3, D1:** The user thought they had to scratch a maximum of three cells, but the game required them to scratch all nine cells which was time consuming and unnecessary according to the users. This is a usability issue that has to be fixed. +> 5. **C1, D3:** The user can get multiple rewards at a time by clicking the spin button multiple times while one spin is still going on. It should be one reward after one spin instead. This is again a usability issue that needs to be fixed. + +#### Submission 3 [(v0.1.4)](https://plguerradesigns.github.io/swe_632_lottery/v0.1.4/) addressed the following usability issues identified in the Think Aloud test: + +1. **B5, C2:** A 'Reset' button was introduced to the Scratch Cards game, allowing users to start a new game after viewing their rewards. +2. **A1, B3:** A rewards tracker was added at the bottom of the game screen, displaying the rewards. +3. **C1, D3:** The spin functionality was disabled while the wheel was spinning to prevent multiple rewards from being displayed simultaneously. + +This submission was then evaluated through a 'Design Critique' session, where we +identified several weaknesses based on design principles learnt thus far in the course and implemented them in the penultimate submission. + +#### Submission 4 [(v0.3.1)](https://plguerradesigns.github.io/swe_632_lottery/v0.3.1/) included the following changes: + +1. Introduce the use of coins to allow users to unlock selected prizes with coins (L7: Keep users in control) +2. Indicate which UI elements can be interacted with (L8: Hinting) +3. Support for different screen sizes including mobile and ultra-wide (L8: Responsive Design) +4. Revise text content to make user actions clear (L8: Clarity of Wording) +5. Add click-to-scratch behavior to enable easier scratch-card gameplay (L8: Supporting Users with Disabilities) +6. Enable always visible scrollbar to allow for drag to scroll (L8: Universal Design) +7. Prevent user from shuffling the wheel prizes while spinning (L9: Preventing Error) + +A second round of 'Design Critique' was conducted to determine any major remaining issues with the app, and the final submission was prepared based on the weaknesses identified. + +#### Submission 5 [(v0.6.1)](https://plguerradesigns.github.io/swe_632_lottery) included the following changes: + +1. The default Flutter icon was replaced with a custom logo for a more unified look and feel. +2. The alignment and size of text elements were adjusted for better readability and consistency. +3. The order of the prizes in the rewards bar was sorted based on the unlock order, showing the most recent prize first. +4. Filter and Sort options were added to the rewards screen to allow users to view prizes based on different criteria. +5. A shimmer effect was added to highlight and draw attention to important interactive game elements. +6. A scale-on-hover effect was added to certain UI elements to provide visual feedback to users. +7. The endgame dialog was replaced with a prize scale animation accompanied by confetti. +8. A new 'Game Analysis' screen was added to display the user's game statistics including, 'Coins Analysis', 'Game Analysis', and 'Reward Analysis'. The user can view the data in a tabular format or chart format. + +## Results + +The final version of the SWE 632 Lottery Web App significantly improved usability and user engagement. Key outcomes include: + +1. **Enhanced Usability:** The addition of a ‘Reset’ button and rewards tracker streamlined navigation, while disabling the spin button during spinning prevented interaction issues. +2. **Improved Interaction:** Introducing coins for prize unlocking and making UI elements more responsive and clear enhanced the gaming experience across various devices. +3. **Visual Refinements:** A custom logo, better text alignment, and visual effects like shimmer, hover scaling, and endgame animations (including confetti) improved the app’s appeal. +4. **Comprehensive Analytics:** The new ‘Game Analysis’ screen provided detailed gameplay statistics, adding depth to the user experience. + +## Discussion + +The iterative development process was crucial in resolving usability concerns and enhancing user interaction. Feedback-driven updates, such as clearer instructions and visual improvements, led to a more refined app. However, a few issues remain, including clipped visuals on smaller devices, which remain to be addressed. + +## Conclusion + +The SWE 632 Lottery Web App evolved into an engaging, user-centric application through continuous user feedback. Hosted on GitHub Pages, it showcases both technical expertise and a strong understanding of user-centered design principles, though there are still areas for further improvement. diff --git a/assets/docs/project_descriptions/switch_speaker.md b/assets/docs/project_descriptions/switch_speaker.md new file mode 100644 index 0000000..a45e6e7 --- /dev/null +++ b/assets/docs/project_descriptions/switch_speaker.md @@ -0,0 +1,3 @@ +The goal of the Nintendo Switch speaker project was to design and build a speaker attachment that would be louder than the Switch's built-in speakers. + +The attachment features a 3.5mm audio plug, dual mini oval speakers, a rechargeable LiPo battery, a micro USB charging port, sliding power switch, power LED, and charging status LED. diff --git a/assets/docs/project_descriptions/xbox_head_tracking.md b/assets/docs/project_descriptions/xbox_head_tracking.md new file mode 100644 index 0000000..f1e4df7 --- /dev/null +++ b/assets/docs/project_descriptions/xbox_head_tracking.md @@ -0,0 +1,5 @@ +The goal of the Xbox Head Tracking project was to design an Xbox compatible head tracking device to track the user's head movements and move the in-game camera. + +The circuit comprises an Arduino microcontroller that reads incoming data from an MPU-6050 (three-axis accelerometer) to track the user's head movements and then controls a digital potentiometer. The output of the digital potentiometer connects to wires added on a modified after-market Xbox controller. The added wires lead directly to the game controller's internal circuit board input for the analog thumbsticks. + +You can find the build guide on Instructables.com. diff --git a/assets/json/professional_experience.json b/assets/json/professional_experience.json index 336b7cb..f0a5932 100644 --- a/assets/json/professional_experience.json +++ b/assets/json/professional_experience.json @@ -1,12 +1,11 @@ [ { "company": "Atlantic Insurance Company Ltd.", - "folderName": "atlantic_insurance", + "source": "atlantic_insurance", "role": "Contract Software Engineer", "location": "(Remote) Belize City, Belize", "startDate": "2020-06-01T00:00:00Z", "endDate": null, - "description": "As a contract software engineer, I led the strategic planning, development, and successful launch of the AIC Mobile App and Management Portal for Atlantic Insurance Company Limited.\n\nThis innovative app, available for Android and iOS, was meticulously designed with a customer-centric approach to enrich interactions with the company.\n\nIts features includes access to insured item details, a digital certificate of insurance, timely renewal notifications, claims filing, quote requests, office location guidance, and more.\n\nThis endeavor reflects AIC's commitment to delivering unsurpassed customer service, upholding integrity, demonstrating professionalism, and fostering teamwork in line with Atlantic Insurance Company Limited's values.", "media": [ { "type": "youTubeVideo", @@ -55,12 +54,11 @@ }, { "company": "AMBOTS Inc.", - "folderName": "ambots", + "source": "ambots", "role": "Lead Robotics Engineer", "location": "Fayetteville, AR", "startDate": "2021-02-01T00:00:00Z", "endDate": "2021-10-01T00:00:00Z", - "description": "As the Lead Robotics Engineer at AMBOTS Inc., I had the privilege of spearheading the transformation of groundbreaking research from the AM³ Lab at the University of Arkansas into a game-changing commercial product. AMBOTS Inc. is dedicated to revolutionizing the future of manufacturing through its patent-pending swarm 3D printing and assembly technologies, which were originally conceived by Dr. Wenchao Zhou in 2015.\n\nOur vision at AMBOTS was nothing short of a manufacturing revolution - a world where production is fully digitized and automated, allowing for the creation of customized products on demand, all at a remarkably low cost. Similar to how a swarm of bees collaborates to build its nest, our smart and autonomous mobile robots were engineered to work seamlessly together, translating digital designs into tangible products.\n\nThe team and I achieved some remarkable milestones some of which include:\n\n• Our groundbreaking work earned us recognition within the industry, including winning the prestigious \"3D Startup of the Year\" by 3D Natives. We were also featured on GE Reports as one of \"The 5 Coolest Things On Earth This Week\" and received coverage on CBS, including an appearance on \"The Henry Ford's Innovation Nation.\"\n\n• We filed patent applications to protect our swarm 3D printing and assembly technology, solidifying our position as pioneers in the field.\n\n• Our dedication and innovation secured Phase I and Phase II of the SBIR grant, totaling $1,225,000 in funding, which fueled our continued growth and development.\n\n• We landed our company's first customer, validating the real-world applicability of our technology.\n\nMy time as the Lead Robotics Engineer at AMBOTS Inc. was marked by a relentless dedication to pushing the boundaries of what's possible in advanced manufacturing. I am proud to have played a pivotal role in bringing the vision of fully automated, on-demand, and cost-effective digital manufacturing closer to reality. Our journey continues, and I look forward to witnessing where the future of this transformative technology takes us.", "media": [ { "type": "youTubeVideo", @@ -262,12 +260,11 @@ }, { "company": "University of Arkansas (AM³ Lab)", - "folderName": "am3_lab", + "source": "am3_lab", "role": "Graduate Research Assistant", "location": "Fayetteville, AR", "startDate": "2019-04-01T00:00:00Z", "endDate": "2020-12-01T00:00:00Z", - "description": "As a Graduate Research Assistant at the AM³ Lab, University of Arkansas, I played a pivotal role in advancing the cutting-edge field of 3D printing technology. The AM³ Lab is dedicated to developing next generation 3D printing technologies and promoting the use of 3D printing technology to better our lives through excellent research, education, and service to the global 3D printing community.\n\nInitially on-boarded as a volunteer, I assisted in troubleshooting and enhancing the 5th-Generation 3D printers. My dedication and problem-solving skills quickly earned me the position of Graduate Research Assistant, where I continued my contributions to the field.\n\nMy primary focus during my time with the lab was developing a groundbreaking swarm 3D printing process designed to enable the production of large-scale structures using a fleet of mobile robots. This innovative approach involved a combination of hardware and software components, including custom-built transporter robots, SCARA 3D printers, and a modular print & power-delivery floor.\n\nMost notably, I was instrumental in transitioning from the 5th-Generation platform to the 6th and 7th-Generations. This transition involved separating the SCARA arm from the mobile base, and optimizing the hardware for improved robustness, efficiency, and quality. I assumed a significant role in these evolutions, taking charge of software development and contributing to hardware enhancements.\n\nOne of my key contributions was the implementation of Computer Vision for localization and navigation within the transporter's control system, enabling semi-autonomous operation - a significant advancement in the field.\n\nNot only was my work at the AM³ Lab a valuable learning experience, but also a contribution to the lab's mission of pushing the boundaries of 3D printing technology. I am proud to have been part of a team dedicated to revolutionizing manufacturing and enabling mass customization through next-generation 3D printing innovations.", "media": [ { "type": "youTubeVideo", diff --git a/assets/json/projects.json b/assets/json/projects.json index 159cf2b..2beabfd 100644 --- a/assets/json/projects.json +++ b/assets/json/projects.json @@ -4,7 +4,6 @@ "subtitle": "For on-the-go Sim Racing", "startDate": "2024-04-14T00:00:00Z", "finalDate": "2024-04-26T00:00:00Z", - "description": "Designed for mobile sim racing, the AMG GT3/GT4 Mobile Sim Wheel kit is a completely 3D-printable steering wheel. It is a remix of one of my earlier and highly popular projects, the AMG GT3 Wheel Kit for Thrustmaster, now optimized for mobile use. The AMG inspired wheel has been shrunk down to 230mm x 130mm, with built in grip handles and slots for attaching a phone mount. The wheel supports mobile games using tilt to steer and tap to accelerate or break.\n\nThe kit itself contains all STL files for the wheel, phone mount, and an assembly manual. Total print time can be completed in under 15 hours (varies based on print settings and materials), while assembly time takes approximately 30 minutes.\n\nVersion 2 of the wheel was designed to more closely resemble the AMG GT3 wheel. The handles were updated to match the actual style and 'fidget' buttons were added for a more realistic look and feel. Several spring mechanisms were tested with different materials to allow for fully 3D-printable parts that can be assembled without the need for additional hardware. The phone clamp was also updated to included thumb cutouts for easier access to the screen during gameplay.", "media": [ { "type": "youTubeVideo", @@ -114,7 +113,6 @@ "subtitle": "A GMU-Themed Lottery Web App", "startDate": "2023-09-03T00:00:00Z", "finalDate": "2023-11-30T00:00:00Z", - "description": "The SWE 632 Lottery Web App, developed for GMU's User Interface Design and Development course, offers an immersive gaming experience with Wheel of Fortune and Scratch Cards, allowing players to earn virtual currency and unlock GMU merchandise. Created in collaboration with Afrina Sharmin and Suzzana Rafi, this project highlights our ability to implement the latest technologies and best practices in user interface design. While it's primarily an academic project, the app is fully functional, exemplifying our team's expertise in developing engaging and user-friendly web applications. We invite you to explore and engage with the app, experiencing our dedication to quality design firsthand.", "media": [ { "type": "localImage", @@ -229,7 +227,6 @@ "subtitle": "Resume Builder Progressive Web App", "startDate": "2022-10-06T00:00:00Z", "finalDate": "2023-10-13T00:00:00Z", - "description": "The Flutter Resume Builder is a user-friendly, open-source progressive web application designed to simplify creating professional resumes. This innovative tool enables users to effortlessly build, edit, and customize their resumes, ensuring they are polished and impactful. With the ability to create, edit, download in both PDF and JSON formats, and import existing resumes, the Flutter Resume Builder offers a comprehensive solution for individuals seeking to craft and maintain their professional documents with ease. Whether you are a job seeker looking to streamline your resume-building process or a developer interested in the project, this platform provides an intuitive and accessible tool for crafting compelling resumes.", "media": [ { "type": "localImage", @@ -294,7 +291,6 @@ "subtitle": "Blokus Multiplayer Web Game", "startDate": "2023-04-15T00:00:00Z", "finalDate": "2023-05-13T00:00:00Z", - "description": "Introducing a real-time multiplayer Blokus game project by Charles Tucker and Pablo L. Guerra, designed using Flutter and Supabase. It offers players the opportunity to engage in multiplayer gaming action with a prototype that utilizes Supabase as the backend and Flame as the game engine. This project was developed as the final project for GMU's ISA 681 class and is not a completed product. Nevertheless, it's entirely usable and serves as a promising foundation for a future real-time multiplayer gaming experience. Explore and play the game to get a firsthand look at its potential. While this project is not currently under active development, it stands as a proof of concept and learning resource, welcoming other developers to build upon its foundation.", "media": [ { "type": "localImage", @@ -366,7 +362,6 @@ "subtitle": "A 3D-Printable Wheel Kit", "startDate": "2018-10-01T00:00:00Z", "finalDate": "2018-10-01T00:00:00Z", - "description": "The goal of the AMG GT3 Wheel project was to design and build a functional, 3D-printed replica of the Mercedes Benz AMG GT3 steering wheel for use in Sim Racing.\n\nAn Arduino Nano powers the wheel and features a thumb-slide joystick, eight pushbuttons, and a pair of paddle shifters. The wheel is directly compatible with the Thrustmaster ecosystem.\n\nYou can find the 3D-printable files on Thingiverse along with an assembly manual, circuit files, and Arduino code.", "media": [ { "type": "youTubeVideo", @@ -565,7 +560,6 @@ "subtitle": "3-in-1 Charging Station", "startDate": "2019-01-01T00:00:00Z", "finalDate": "2019-01-01T00:00:00Z", - "description": "The Apple Charging Station, initially sketched in 2016, was later revived and given as a gift to a friend.\n\nRedesigned in Autodesk Fusion 360, printed using wood-infused PLA, and finished with a wood stain. Supports charging of an iPhone, Apple Watch, and the original AirPods. The charging modules are interchangeable to support different product dimensions.", "media": [ { "type": "localImage", @@ -627,7 +621,6 @@ "subtitle": "Codemaster's F1 2019 Companion App", "startDate": "2019-10-01T00:00:00Z", "finalDate": "2019-12-01T00:00:00Z", - "description": "F1 Sim Engineer, co-developed with Viktor Soendergaard for a mobile programming class at the University of Arkansas, is a companion app for the F1 2019 sim racing game.\n\nThe app is written in Java and uses the UDP protocol to communicate with the gaming device to receive and display live telemetry data sent from the F1 2019 game. The app obtains various data from the game, including event information, race details, number of laps, number of participants, weather conditions, vehicle setup, vehicle status, and telemetry. This information is displayed live during the race is saved to the local database for later review.", "media": [ { "type": "localImage", @@ -668,7 +661,6 @@ "subtitle": "Nintendo Switch Speaker Attachment", "startDate": "2019-02-01T00:00:00Z", "finalDate": "2019-02-01T00:00:00Z", - "description": "The goal of the Nintendo Switch speaker project was to design and build a speaker attachment that would be louder than the Switch's built-in speakers.\n\nThe attachment features a 3.5mm audio plug, dual mini oval speakers, a rechargeable LiPo battery, a micro USB charging port, sliding power switch, power LED, and charging status LED.", "media": [ { "type": "localImage", @@ -744,7 +736,6 @@ "subtitle": "Homemade Sim Racing Rig", "startDate": "2015-06-01T00:00:00Z", "finalDate": "2018-01-01T00:00:00Z", - "description": "The goal of Project LeMans was to build a relatively low-budget sim racing rig out of easy-to-access parts and materials. It needed to have the basic functionality of a sim racing wheel and pedal set.\n\nIt features a Teensy for the brains, emulating a gaming controller and supporting input from the steering wheel's rotation, its two buttons, pair of paddle shifters, as well as the throttle, brakes, and clutch located on the pedal base.", "media": [ { "type": "localImage", @@ -904,7 +895,6 @@ "subtitle": "Concept Self Destructing Storage Device", "startDate": "2018-04-01T00:00:00Z", "finalDate": "2018-05-01T00:00:00Z", - "description": "The goal of the SafeDrive MK1 project was to design a secure storage device that unlocked using biometric authentication and is capable of self-destructing.\n\nIt features an Arduino micro-controller, an RGB backlight LCD Panel, a TTL fingerprint scanner, an interchangeable USB flash drive slot, and a USBKill device. If the device recognizes a fingerprint, it will provide access to the internal USB flash drive. However, after ten failed attempts, the Arduino enables the USBKill device, releasing close to 200 negative volts (in the reverse direction), destroying the internal drive's data lines and any unprotected connected devices.", "media": [ { "type": "localImage", @@ -1006,7 +996,6 @@ "subtitle": "Gyroscopic Head Tracking Device", "startDate": "2018-02-01T00:00:00Z", "finalDate": "2018-04-01T00:00:00Z", - "description": "The goal of the Xbox Head Tracking project was to design an Xbox compatible head tracking device to track the user's head movements and move the in-game camera.\n\nThe circuit comprises an Arduino microcontroller that reads incoming data from an MPU-6050 (three-axis accelerometer) to track the user's head movements and then controls a digital potentiometer. The output of the digital potentiometer connects to wires added on a modified after-market Xbox controller. The added wires lead directly to the game controller's internal circuit board input for the analog thumbsticks.\n\nYou can find the build guide on Instructables.com.", "media": [ { "type": "youTubeVideo", @@ -1079,7 +1068,6 @@ "subtitle": "Calculator App for Android", "startDate": "2019-09-01T00:00:00Z", "finalDate": "2019-09-01T00:00:00Z", - "description": "Android Calculator is a calculator app developed for a mobile programming class assignment at the University of Arkansas.\n\nThe app was designed in Photoshop using popular calculator apps for design reference, then programmed in Java using Android Studio.", "media": [ { "type": "localImage", @@ -1118,7 +1106,6 @@ "subtitle": "Programmable Macro Pad (HID)", "startDate": "2019-04-01T00:00:00Z", "finalDate": "2019-04-01T00:00:00Z", - "description": "Macro Pad is a programmable interface used for executing custom macros (button combinations or input sequences). It allows for extended control and execution of complex commands with a single button press.\n\nThe device features an Arduino Micro, 16 Cherry MX switches, a rotatable and pressable knob, and a micro USB port for connecting.", "media": [ { "type": "localImage", @@ -1158,7 +1145,6 @@ "subtitle": "Smart LED Wall Decor", "startDate": "2019-02-01T00:00:00Z", "finalDate": "2019-02-01T00:00:00Z", - "description": "The LED wall decor was made as an art piece for a friend. The signs' colors and patterns are controllable using the Blynk app.\n\nIt features an Arduino MKR1000 controlling eight RGB LEDs and connects to the internet using its onboard WiFi module. The LED cover plate is interchangeable using magnets. A USB micro cable is used for continuous power, but the device can also be powered using a 3.7V LiPo battery and charged with the MKR's onboard charging circuit. One of the LEDs was not working at the time the photos were taken.", "media": [ { "type": "localImage", @@ -1200,7 +1186,6 @@ "subtitle": "Bionic Arm and Input Glove", "startDate": "2012-09-01T00:00:00Z", "finalDate": "2013-01-01T00:00:00Z", - "description": "The Handroid project was a science fair submission for my junior year of high school. The idea was inspired by arms developed in medical fields, where mechanical arms can mimic finger movements.\n\nThe shape of the arm was made using foam, cardboard, pieces of wood, and fiberglass. Rubber tubes are used for the fingers with incisions made at the bending points. The fingertips are each connected to a fishing line, which routes internally through the finger and arm, then attaches to the horn of a servo motor. An Arduino microcontroller controls the servo motors and receives input from a modified glove with flex sensors attached to each finger. When the input glove is worn, the flex sensors record the amount of bending of each finger which the Arduino uses to determine the degree of rotation for each servo motor to replicate the users' finger movements.", "media": [ { "type": "localImage", diff --git a/lib/models/professional_experience.dart b/lib/models/professional_experience.dart index f665f53..c4c3df8 100644 --- a/lib/models/professional_experience.dart +++ b/lib/models/professional_experience.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; import 'package:unorm_dart/unorm_dart.dart' as unorm; @@ -13,15 +14,14 @@ import 'media_item.dart'; class ProfessionalExperience { ProfessionalExperience({ required this.company, - required this.folderName, + required String source, required this.role, required this.location, required this.startDate, required this.finalDate, required this.mediaItems, - required this.description, required this.externalLinks, - }); + }) : _source = source; /// Creates a [ProfessionalExperience] from a JSON object. factory ProfessionalExperience.fromJson(Map json) { @@ -36,16 +36,15 @@ class ProfessionalExperience { if (mediaItem.type == MediaType.localImage || mediaItem.type == MediaType.localVideo) { mediaItems[i].source = - 'assets/images/professional/${json['folderName'].toString().toLowerCase().replaceAll(' ', '_')}/${mediaItem.source}'; + 'assets/images/professional/${json['source'].toString().toLowerCase().replaceAll(' ', '_')}/${mediaItem.source}'; } } return ProfessionalExperience( company: json['company'].toString(), - folderName: json['folderName'].toString(), + source: json['source'].toString(), role: json['role'].toString(), location: json['location'].toString(), - description: json['description'].toString(), startDate: DateTime.parse(json['startDate'].toString()), mediaItems: mediaItems, finalDate: json['endDate'] == null @@ -63,8 +62,8 @@ class ProfessionalExperience { /// The name of the company. final String company; - /// The name of the folder. - final String folderName; + /// The source of the professional experience. + final String _source; /// The name of the company and role as a path String get titleAsPath => unorm.nfkc( @@ -83,7 +82,8 @@ class ProfessionalExperience { final DateTime? finalDate; /// The description of the experience. - final String description; + String get descriptionSource => + 'assets/docs/professional_descriptions/${_source.toLowerCase().replaceAll(' ', '_')}.md'; /// Relevant external links. final List> externalLinks; @@ -95,7 +95,7 @@ class ProfessionalExperience { int get totalMediaCount => mediaItems.length; /// The base path for the media. - String get baseMediaPath => 'assets/images/professional/$folderName/'; + String get baseMediaPath => 'assets/images/professional/$_source/'; /// The path for the thumbnail. String get thumbnailPath => '${baseMediaPath}thumbnail.webp'; @@ -138,7 +138,7 @@ class ProfessionalExperience { subtitle: role, appBarTitle: Strings.professionalExperiences, logoPath: logoPath, - description: description, + descriptionSource: descriptionSource, externalLinks: externalLinks, startDate: startDateString, endDate: finalDateString, diff --git a/lib/models/project.dart b/lib/models/project.dart index 74e0f6d..0c9546f 100644 --- a/lib/models/project.dart +++ b/lib/models/project.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; import '../common/enums.dart'; @@ -13,7 +14,6 @@ class Project { Project({ required this.title, required this.subtitle, - required this.description, required this.startDate, required this.finalDate, required this.externalLinks, @@ -41,7 +41,6 @@ class Project { return Project( title: json['title'].toString(), subtitle: json['subtitle'].toString(), - description: json['description'].toString(), startDate: DateTime.parse(json['startDate'].toString()), finalDate: DateTime.parse(json['finalDate'].toString()), mediaItems: mediaItems, @@ -61,6 +60,13 @@ class Project { /// The subtitle of the project. final String subtitle; + /// The source of the professional experience. + String get _source => title.toLowerCase().replaceAll(' ', '_'); + + /// The source of the project. + String get descriptionSource => + 'assets/docs/project_descriptions/$_source.md'; + /// The title of the project as a path. String get titleAsPath => title.toLowerCase().replaceAll(' ', '-'); @@ -70,9 +76,6 @@ class Project { /// The final date of the project. final DateTime finalDate; - /// The description of the project. - final String description; - /// Relevant external links. final List> externalLinks; @@ -86,8 +89,7 @@ class Project { int get totalMediaCount => mediaItems.length; /// The base path for the media. - String get baseMediaPath => - 'assets/images/personal/${title.toLowerCase().replaceAll(' ', '_')}/'; + String get baseMediaPath => 'assets/images/personal/$_source/'; /// The path for the thumbnail. String get thumbnailPath => '${baseMediaPath}thumbnail.webp'; @@ -121,7 +123,7 @@ class Project { titleAsPath: titleAsPath, appBarTitle: Strings.personalProjects, subtitle: subtitle, - description: description, + descriptionSource: descriptionSource, externalLinks: externalLinks, startDate: startDateString, endDate: finalDateString, diff --git a/lib/pages/details/details.controller.dart b/lib/pages/details/details.controller.dart index 6b80c21..09f1bce 100644 --- a/lib/pages/details/details.controller.dart +++ b/lib/pages/details/details.controller.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:go_router/go_router.dart'; import '../../common/routing/routes.dart'; @@ -20,6 +21,7 @@ class DetailsController extends ChangeNotifier { isMediaBrowserOpen: _appState.mediaBrowserOpen, updateMediaBrowserVisibilityState: _appState.toggleMediaBrowserVisibility, ); + _loadMarkdownData(); } /// The application state. @@ -41,7 +43,7 @@ class DetailsController extends ChangeNotifier { String get title => _details.title; /// The description of the project/experience. - String get description => _details.description; + String description = ''; /// The external links to display. List> get externalLinks => _details.externalLinks; @@ -55,6 +57,16 @@ class DetailsController extends ChangeNotifier { /// Whether the media browser is open. bool get mediaBrowserOpen => _appState.mediaBrowserOpen; + Future _loadMarkdownData() async { + try { + description = await rootBundle.loadString(_details.descriptionSource); + } catch (e) { + debugPrint(e.toString()); + description = 'Description not available.'; + } + notifyListeners(); + } + /// The callback for navigating to the previous project/experience. void onPreviousPressed(BuildContext context) { String route = ''; diff --git a/lib/pages/details/details.model.dart b/lib/pages/details/details.model.dart index 14eafc3..6bc84ab 100644 --- a/lib/pages/details/details.model.dart +++ b/lib/pages/details/details.model.dart @@ -6,7 +6,7 @@ class Details { required this.title, required this.titleAsPath, required this.subtitle, - required this.description, + required this.descriptionSource, required this.mediaItems, required this.tags, required this.externalLinks, @@ -36,8 +36,8 @@ class Details { /// The end date of the project/experience. final String? endDate; - /// The description of the project/experience. - final String description; + /// The source of the description. + final String descriptionSource; /// The list of media items to display. final List mediaItems; diff --git a/lib/pages/details/details.screen.dart b/lib/pages/details/details.screen.dart index 46bc8c1..9e26264 100644 --- a/lib/pages/details/details.screen.dart +++ b/lib/pages/details/details.screen.dart @@ -8,6 +8,7 @@ import 'details.controller.dart'; import 'details.model.dart'; import 'widgets/app_bar_actions.dart'; import 'widgets/info_header.dart'; +import 'widgets/markdown_view.dart'; import 'widgets/media_player/multi_media_player.controller.dart'; import 'widgets/media_player/multi_media_player.dart'; import 'widgets/more_info.dart'; @@ -71,10 +72,8 @@ class DetailsScreenState extends State { compact: true, ), const Divider(height: 32), - SelectableText( - controller.description, - style: Theme.of(context).textTheme.bodyMedium, - textAlign: TextAlign.justify, + MarkdownView( + markdownData: controller.description, ), if (controller.externalLinks.isNotEmpty) MoreInfo(externalLinks: controller.externalLinks), @@ -153,9 +152,8 @@ class DetailsScreenState extends State { compact: false, ), const Divider(height: 32), - SelectableText( - controller.description, - style: Theme.of(context).textTheme.bodyMedium, + MarkdownView( + markdownData: controller.description, ), if (controller.externalLinks.isNotEmpty) MoreInfo(externalLinks: controller.externalLinks), diff --git a/lib/pages/details/widgets/markdown_view.dart b/lib/pages/details/widgets/markdown_view.dart new file mode 100644 index 0000000..246da6f --- /dev/null +++ b/lib/pages/details/widgets/markdown_view.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:markdown_widget/markdown_widget.dart'; + +import '../../../widgets/spinner.dart'; + +/// A widget that displays markdown data. +class MarkdownView extends StatefulWidget { + const MarkdownView({ + super.key, + required this.markdownData, + }); + + /// The markdown data to display. + final String markdownData; + + @override + State createState() => _MarkdownViewState(); +} + +class _MarkdownViewState extends State { + @override + Widget build(BuildContext context) { + if (widget.markdownData.isEmpty) { + return const Spinner(); + } + return MarkdownBlock( + data: widget.markdownData, + config: MarkdownConfig( + configs: [ + CustomH2Config( + context: context, + style: Theme.of(context).textTheme.titleMedium!, + ), + PConfig( + textStyle: Theme.of(context).textTheme.bodyMedium!, + ), + ], + ), + ); + } +} + +/// A custom configuration for the H2 heading. +class CustomH2Config extends H2Config { + const CustomH2Config({ + super.style = const TextStyle(), + required this.context, + }); + + final BuildContext context; + + @override + HeadingDivider? get divider => HeadingDivider( + color: Theme.of(context).colorScheme.outlineVariant.withOpacity(0.3), + height: 0.01, + ); +} diff --git a/pubspec.lock b/pubspec.lock index 67b9f21..b8aa83c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -126,6 +126,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_highlight: + dependency: transitive + description: + name: flutter_highlight + sha256: "7b96333867aa07e122e245c033b8ad622e4e3a42a1a2372cbb098a2541d8782c" + url: "https://pub.dev" + source: hosted + version: "0.7.0" flutter_lints: dependency: "direct dev" description: @@ -176,6 +184,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.1.0" + highlight: + dependency: transitive + description: + name: highlight + sha256: "5353a83ffe3e3eca7df0abfb72dcf3fa66cc56b953728e7113ad4ad88497cf21" + url: "https://pub.dev" + source: hosted + version: "0.7.0" html: dependency: transitive description: @@ -256,6 +272,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + markdown: + dependency: transitive + description: + name: markdown + sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051 + url: "https://pub.dev" + source: hosted + version: "7.2.2" + markdown_widget: + dependency: "direct main" + description: + name: markdown_widget + sha256: "216dced98962d7699a265344624bc280489d739654585ee881c95563a3252fac" + url: "https://pub.dev" + source: hosted + version: "2.3.2+6" matcher: dependency: transitive description: @@ -440,6 +472,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.2" + scroll_to_index: + dependency: transitive + description: + name: scroll_to_index + sha256: b707546e7500d9f070d63e5acf74fd437ec7eeeb68d3412ef7b0afada0b4f176 + url: "https://pub.dev" + source: hosted + version: "3.0.1" sensors_plus: dependency: transitive description: @@ -677,6 +717,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + visibility_detector: + dependency: transitive + description: + name: visibility_detector + sha256: dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420 + url: "https://pub.dev" + source: hosted + version: "0.4.0+2" vm_service: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index dcb7e6f..ca13a69 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: plg_portfolio description: Pablo L. Guerra's web-app portfolio powered by Flutter. publish_to: "none" -version: 2.7.0+64 +version: 2.8.0+65 environment: sdk: ">=3.1.1 <4.0.0" @@ -27,6 +27,7 @@ dependencies: flutter_spinkit: ^5.2.1 package_info_plus: ^8.0.2 flutter_tilt: ^3.0.3 + markdown_widget: ^2.3.2+6 dev_dependencies: flutter_test: @@ -37,8 +38,9 @@ flutter: uses-material-design: true assets: - - assets/docs/ - assets/json/ + - assets/docs/project_descriptions/ + - assets/docs/professional_descriptions/ - assets/images/home/ - assets/images/icons/ - assets/images/professional/atlantic_insurance/