-
Notifications
You must be signed in to change notification settings - Fork 1
Sprint 4
In this YouTube link you can check our ethical reflection about this sprint.
The value proposition of our mobile app is to provide Uniandes students with a convenient and efficient platform to connect with like-minded individuals, overcome barriers to practicing sports, and engage in regular physical activity. The implemented features, business questions, revenue model, and collected data contribute to the overall value proposition in the following ways:
1. Addressing the Problem: The app addresses the problem of "Difficulties finding people to practice a sport" by offering a solution that connects users with others who share similar interests. The implemented features such as Home View, List Events view, and Create Event view facilitate easy and efficient interaction between users, allowing them to find sports partners, organize events, and join existing activities. By providing a platform for users to connect, the app helps overcome social barriers and enables users to engage in physical activities more readily.
2. Convenience and Efficiency: The app offers a range of features to enhance convenience and efficiency for users. The Home View provides a centralized dashboard where users can access various tools and information, making it easier to navigate the app and find relevant content. The Profile View allows users to customize their preferences and display their sports interests, facilitating targeted connections with like-minded individuals. The List Events view and Create Event view enable users to explore and schedule activities conveniently, streamlining the process of finding and organizing sports events.
3. Data-Driven Insights: The implementation of business questions allows the app to gather relevant data about user preferences, sports distribution, and desired features. By analyzing the distribution of users per sport, the app can provide insights into popular sports and tailor its offerings accordingly. The most used feature analysis helps identify the app's key functionality and optimize its user experience. Understanding the most requested or desired features by users looking for sports partners allows the app to prioritize and enhance those features to meet user expectations. Identifying sports highly requested by users but not yet available in the app can guide future expansion plans. Additionally, monitoring the success rate of event creators in bringing the expected number of participants helps ensure the reliability and quality of organized activities.
4. Revenue Model: The proposed freemium subscription-based revenue model provides users with a free version of the app that offers essential features, while premium features are available through a subscription fee. This model allows users to access basic functionality without barriers, while generating revenue from those who desire additional benefits and personalized recommendations. The revenue generated through sponsorships or partnerships with local sports centers and related companies further supports the sustainability and growth of the app.
5. Target Audience and Device Compatibility: The collected data on the distribution of devices and target users using the app provides insights into the user demographics and preferences. Understanding the devices used by the target audience helps optimize the app's compatibility and user experience across various platforms. Additionally, knowing the interests and preferences of the app's users enables effective targeting of marketing campaigns and potential partnerships.
In summary, the app's value proposition lies in its ability to connect Uniandes students with like-minded individuals, overcome barriers to practicing sports, and foster a sense of community. The implemented features, business questions, revenue model, and collected data contribute to the app's convenience, efficiency, personalization, data-driven insights, and revenue generation, ultimately addressing the problem of difficulties in finding people to practice a sport and promoting improved physical and mental health among users.
For the case of the flutter team, we conducted a profiling and hardware analysis to identify the frequently used features within our app that could potentially pose performance issues. This was done with the intent of solving these issues by implementing micro optimization strategies and addressing any potential bottlenecks within the FitConnect app.
The features analyzed here were:
- BPM Sensor
- My Stats View
- Creating events with new Map
- Emergency call feature
These are the results we got running the profiling tool and before applying microoptimizations to the app:
- GPU Rendering
- Memory Consumption
- CPU Tracing and Execution times
Overall we noticed that this feature has good rendering times but it is a CPU intensive scenario due to constant image processing and and infinite iteration (given that the user doesn't stop measuring) with a main processing while loop. It also had a lot of garbage collection events indicating that a lot of data is being generated. In order to improve this, since the sensor code was already optimized we decided to increase the sampling window and decrease the amount of data taken but in a lesser proportion compared to the window length.
We changed the code from these parameters (30 samples every 6 seconds, around 5 samples per second):
{width="600"}
To these (35 samples every 10 seconds, around 3.5 samples per second):
{width="600"}
- CPU Tracing and Execution times
- Memory Consumption
While the memory consumption didn't really change (GC events happened at the same rate) and the amount of RAM used was literally the same, we noticed a slight improve in performance in around 100ms overall (CPU times) in the whole scenario. We have to note however, that we tried to increase the window size even more, but the measurements turned into a slow process and rather more inaccurate compared to their more time consuming counterparts.
- GPU Rendering
- Memory Consumption
- CPU Tracing and Execution times
In the case of the personal statistics view, we identified that there was a good general performance with good memory consumption and short CPU execution times. However, we noticed that in the code the loops that calculated the bar plots had variables instantiated within the loop iteration as can be seen below:
{width="600"}
Therefore we decided to eliminate this redundant memory allocation to see how the app performs. This is the optimized code snippet:
{width="600"}
- Memory Consumption
- CPU Tracing and Execution times
In the case of this scenario, the CPU execution times were decreased overall leading to a better performance than before. We can note that the longest element, the reportTaskEvent took a few miliseconds more to execute, but the rest of the items had in average a lower execution time compared to before optimizing. On the side of the memory consumption, we noticed that the average allocated size for lists and objects in memory was decreased by about 0.3 MB, which it might not sound as much, but in terms of mobile phones performance, every small bit counts.
- GPU Rendering
- Memory Consumption
- CPU Tracing and Execution Times
In the case of the event creation with map, just as before we noticed that the performance was good overall without having any deep memory allocations or high CPU execution times. We noticed however a small increase in GPU render times due to showing an interactive map on the screen compared to static widgets.
On the side of possible optimizations, we noticed that this screen was implemented with direct stateful widgets before, causing a greater impact on the device due to how flutter manages these kind of widgets and their state within the compiler. To change and improve this, we decided to move all the state related elements and functions to the screen viewmodel so that the stateful logic could be handled more easily in the background via Providers.
- Code before the change (State in widget)
{width="600"}
- Code after: (State moved to Viewmodel with provider)
{width="600"} {width="600"}
- Memory Consumption
- CPU Tracing and Execution times
We can see when comparing the previous images with the ones before optimizing that in terms of CPU execution times, the overall timings had positive results with improvements of over 20 to 15 ms per item executed. Aditionally, when comparing memory consumption we also had a great improvement regarding lists and strings allocated in memory, however we can also see that the amount of GC events increased as well. This could be due to how providers work in terms of updating items. Here we can note an interesting tradeoff, better execution times and memory allocation in exchange for an increased number of GC events.
- GPU Rendering
- Memory Consumption
- CPU Tracing and Execution times
In the case of this feature we identified that in general it has higher GPU rendering times due to the LottieFile animations, which is an expected behavior of these widgets. On another hand, in the memory consumption we noticed a bunch of GC events, but a relatively low amount of memory allocated similar to how the create event with map scenario worked. When analyzing the code, we noticed that the shared preferences instance was being retrieved multiple times from disk within the same execution, so for the case of this feature, we decided to solve it by calling it once and making it a property of the respective viewmodel class.
{width="600"}
{width="600"} {width="600"}
- Memory Consumption
- CPU Tracing and Execution times
We noticed that after these optimizations, the overall CPU execution times greately improved with results of 50 - 80 ms lower execution times. This showed that accessing the disk multiple times was costly for the hardware. On another hand, regarding memory consumption, we identified that the memory allocation increased, this could be due to holding the shared preferences instance all the time compared to calling it multiple times, but not storing it after. This is a clear example of a tradeoff where we sacrifice RAM in order to get faster execution times.
When we were performing profiling in dart devtools we noticed that there were some instances of JIT shader compilation warnings that suggested us to precompile them before building the app. These messages looked like the following one:
We did execute the dart command, however we were unable to test it due to only being able to precompile them for the final release build. The command used was flutter build apk --bundle-sksl-path flutter_01.sksl.json
For the case of the java team, we conducted a profiling and hardware analysis to the most important features on our app. This was done with the intent of solving these issues by implementing micro optimization strategies and addressing any potential bottlenecks within the FitConnect app.
The features analyzed here were:
- EventsDistribution View
- Home View
- CreateEvent View
{width="600"}
{width="600"}
- CPU and RAM usage
{width="430"}
- Data binding
- The use of centralized color resources in XML files
{width="600"}
{width="600"}
- CPU and RAM usage
{width="430"}
After analyzing the data returned by the simulator, it was possible to observe that the implementation of the micro-optimizations in the EventsDistributionActivity class had a positive impact on the performance of the application. Specifically, it was possible to verify that the RAM consumption remained constant before and after accessing the view, which indicates efficient resource management and avoids possible memory problems. In addition, a decrease in the number of spikes in CPU usage was observed, indicating a reduction in processor workload and better resource allocation.
These results support the choice to use Data Binding and color resources instead of static codes as micro-optimizations. The use of Data Binding allowed direct and efficient binding of UI components to class variables and methods, eliminating the need to manually find and assign views. This reduced code overhead and optimized performance by avoiding unnecessary data type conversions and repetitive operations. On the other hand, the use of centralized color resources in XML files allowed a more efficient management of the colors used in the application. By accessing colors through their ID, the creation and management of multiple color objects at runtime was avoided, which contributed to a reduction in memory consumption and CPU load.
In summary, the results obtained show that the micro-optimizations implemented in the EventsDistributionActivity class, such as the use of Data Binding and color resources, have significantly improved the performance of the application. These practices have optimized resource management, decreased CPU load, and ensured a smoother user experience.
- CPU Tracing and Excecution Time
- Memory consumption Even though, we have not foudn something alarming in our profiling tools, after checking the code related to Home View, we found out a few code parts that could be optimized in order to improve performance.
-
Use of global variables that could be local
In this case sportsList, sportViewModel, toolBar, statsButton and createEventButton could be final variables located on the method that are used.
-
Unused imported elements
There are some elements that are imported but are never used. In this case java.util.Objects and android.content.DialogInterface
-
Click listeners that could be replaced by lambda functions
- CPU Tracing and execution time
- Memory consumption
- Use of global variables that could be local
- Unused imported elements: Elements showed were deleted from HomeActivity file.
- Click Listeners that could be replaced by lambda functions
CPU Tracing and execution time Memory consumption Even though, we have not found something alarming in our profiling tools, after checking the code related to CreateEvent View, we found out a few code parts that could be optimized in order to improve performance.
- Unused import statements: There were a bunch of import statements of unused elements, we are going to delete them.
CPU Tracing and execution time Memory consumption
- Unused import statements: Unused import statements were deleted. So compile time is reduced.
- Home View
- Stats view and features (Type 2 BQs display)
- Profile View
- BPM Monitor view and features
- Firebase Singleton implementation
- Leave event feature
- Assigned issues
- Merge requests
-
BPM Measurement feature (now the BPM data is stored locally in Realm)
-
Settings View (Settings are stored in Shared Preferences)
-
RealmDB Cleanup feature (Isolates)
-
Business Question Implemented:
What is the distribution of users per sport? (Type 4)This business question was implemented via GCP Cloud Functions and displayed in a Google Data Studio dashboard:
- Cloud Functions source code: https://gitlab.com/ISIS3510_202310_Team24/Analytics_Engine/-/blob/main/functions/src/index.ts
- Dashoard Link: https://lookerstudio.google.com/reporting/12d7ac50-3afd-4940-94f4-78c609f58695
-
Business Question Implemented:
What is the distribution of devices and target users that use our app and how are they using it? (Which kind of people are interested in looking for sports, which devices do they use and what do they do with it)(Type 5) [Type 3 - Features analysis and Type 4 - Benefits from data]
On one hand we can use this information to target specific groups with relevant advertising or partner with companies that cater to those groups of users, it also helps knowing which type of users are spending more time in our application and why.
This business question was implemented via Firebase Analytics, data collected in BigQuery and displayed in a Google Data Studio dashboard:
-
Google Data Studio Dashboard:
{width="800"}
Link to the dashboard: https://lookerstudio.google.com/reporting/e88d61eb-d979-430d-9e2f-c78af6db9a0d
-
New: Exercise List View (using a custom manager from Flutter Cache Manager to cache)
The Exercise List View is a new addition to the app that provides users with a curated list of exercises they can engage in to improve their fitness and well-being. Powered by a custom manager built on top of Flutter Cache Manager, the exercise list is efficiently cached to ensure fast and seamless access even in offline or low connectivity situations. Users can browse through a wide variety of exercises that can be performed in various settings, allowing them to stay active and healthy wherever they are. -
New: Exercise filtering and latest exercises stored (usage of shared preferences to handle filtering)
With the exercise filtering feature, users have more control over the type of exercises they want to explore. By utilizing shared preferences, the app remembers the user's filtering preferences, allowing them to easily discover exercises that align with their specific goals or interests. Whether users prefer cardio workouts, strength training, yoga, or any other form of exercise, they can tailor the exercise list to suit their preferences. Additionally, the app stores the latest exercises viewed by the user, providing a convenient way to revisit previously explored exercises. -
Eventual Connectivity Strategies:
- There were EC strategies implemented of the type "cache, falling back to network" data fetching strategy.
- New: The exercise list works by using a custom cache manager that checks if the requested exercise is stored locally or if it needs to be retrieved from the network. In case it is not stored, the manager will reach out to the API and return the response to the user, then storing the response in cache.
- There were EC strategies implemented of the type "cache, falling back to network" data fetching strategy.
-
Local Storage Strategies:
- As local storage strategy I used Shared Preferences:
- New: User exercise choices and last seen exercises are stored in Shared Preferences.
- As local storage strategy I used Shared Preferences:
-
Multithreading Strategies: The multithreading strategies involved the use of futures and asynchronous functions, specifically when designing the new cache manager:
- New: Custom API cache manager built on top of the "Flutter Cache Manager" library that uses futures to properly handle API requests made to the exercise API. Additionally the creation of the exercise list is done via an asynchronous function that executes when the main thread is free.
-
Caching Strategies: The caching strategies used relied on a custom cache manager built on top of the Flutter Cache Manager package. Additionally to store the images for each exercise we also used CachedNetworkImage. The functionalities that use caching are:
- Exercise List (Custom Cache Manager)
- Exercise Images (Cached Network Image)
- Authentication view and features (Login/signup)
- List Events view
- User model, DTO and Repository implementation
- Icon selection and importing
- Firebase Performance Dashboard Integration: (BQ type 1) Which are the local features that take the longest time to execute compared to the average execution time? Which part in the code is causing this performance impact?
- Assigned issues
- Merge requests
- Local time feature on List Events.
- Add "Turn Off Autoreload from List Events" setting (Settings are stored in Shared Preferences).
- New events autoreload feature on List Events (Stream).
- Assigned issues
- Merge requests
-
Business Question Implemented:
Which is the most used feature and why is it the most used? (Type 3) This business question was implemented by using BigQuery data warehouse and displayed in a Google Data Studio dashboard. Link to the dashboard: https://lookerstudio.google.com/reporting/9d320892-5b22-407d-900c-43723cffb673/page/rgIKD
-
Business Question Implemented:
What is the distribution of emergencies by type and country? This data analysis can provide valuable insights for emergency response planning and resource allocation. Additionally, it can be of interest to, for example, insurance companies in deciding which types of insurance to offer in different countries. (Type 4)This business question was implemented by using BigQuery data warehouse and displayed in a Google Data Studio dashboard:
-
Google Data Studio Dashboard:
Link to the dashboard: https://lookerstudio.google.com/reporting/0edddd27-f892-4027-adb7-7c2054e683c3
-
New Emergency Requests Feature: the emergency screen includes a dropdown menu where the user can select the reason for the emergency. When the user slides the button, it triggers the emergency view model. The view model collects various information such as the user's name, location, reason, timestamp, and sets the status as "PENDING". The view model utilizes the emergency repository, emergency model, and emergency DTO to handle the data. Caching is made at repository on getEmergency(). If there is no internet connection request is enqueued on cache.
After the request is sent, emergency help request ID is stored on local storage, in order to know if there is already a pending request when user closes app, and the emergency screen transitions to a state where it is waiting for administrator approval. Simultaneously, the administrator receives a notification in their Svelte dashboard, indicating a new emergency request. The administrator reviews the request and can choose to approve or ignore it.
Meanwhile, the user listens for the administrator's approval using a stream in the emergency service. Once the request is approved, the emergency screen updates to reflect the approval and displays a message indicating that the administrator is on their way.
This flow allows users to request emergency help through the emergency screen, administrators to manage and respond to those requests in the Svelte dashboard, and users to receive notifications and updates regarding the status of their emergency request.
- Sequence Diagram:
- New Firebase Hosted Dashboard: You can find the administrators dashboard at this link: https://fitconnect-admin.web.app
{width="800"}
-
New feature animations optimizations:
- New: Lottie files are employed in the emergency screen to enhance the main animations. These files are highly efficient, typically ranging from 10-20KB in size. In comparison, animations in JSON format are approximately 10 times larger, and GIF files perform even worse in terms of performance and size. By utilizing Lottie files, we ensure optimal animation quality while keeping the overall file size and performance impact to a minimum. For more information, check out: https://lottiefiles.com/what-is-lottie
{width="400"} {width="400"} {width="600"}
- New Implementations for Emergencies Feature: Created new DTO, Model, Repository, View Model, Screen, Service, and Admin Dashboard.
-
Eventual Connectivity Strategies:
- There were EC strategies implemented of the type "network falling back to cache" data fetching strategy.
- New: Enqueue help request on emergencies, if there is no connection, notify user of this, and put on waiting approval state. When connection is reestablished, automatically send the emergency request to Firestore DB.
- New: Get emergencies request status from cache, when there is no internet connection.
- There were EC strategies implemented of the type "network falling back to cache" data fetching strategy.
-
Local Storage Strategies:
- As local storage strategy I used Shared Preferences:
- New: Last emergency request ID is stored in Shared Preferences, in order to know if there is already a pending request.
- New: Boolean isEmergencyRequestEnqueued is stored in Shared Preferences, in case that the user closes the application before sending help requests to the remote database. The app sends automatically a new request when app is opened again instead of asking again to create the request manually.
- As local storage strategy I used Shared Preferences:
-
Multithreading Strategies: The multithreading strategies used were Futures, async functions, and Streams, the specific features that had these strategies implemented were:
- New: Emergency Service creates a Stream to know if an administrator has approved a pending help request. This is done by subscribing to a snapshot on Firebase and listening to changes. When, the user emergency request is approved the service informs the Emergency ViewModel that is necessary to change the screen status for showing the user this approval notification.
- New: Creation of sendHelpRequest() Future, that manages all the operations after user slides the emergency request button.
- New: The Emergency Service is initialized through a custom async function on Emergency View Model.
-
Caching Strategies:
The caching strategies used relied mostly on Firebase's own caching system, however it was modified so data retrieval from cache was done manually instead of automatically. The functionalities that use caching are:- Emergency Repository (Firebase Caching).
- Create Event view and features
- Event details view and features
- Event model, DTO and Repository implementation
- Join event feature
- (BQ type 1) How many times has the app crashed over the past week? Which elements are causing the crashes?
- Assigned issues
- Merge requests
- Smart Feature
-
Help/Feedback Screen
-
feature in Create event that allows the user to save and load an event tameplate (Shared Preferences).
-
Weather Notification on Create Event.
-
Business Questions Implemented:
What are the most requested or desired features by users who are looking for sports partners? (Type 3)Is there a sport that is highly requested by users but not yet available in the app? (Type 3) Link to the dashboard: https://docs.google.com/spreadsheets/d/1gXQMKMA4ywvonC7Pf4BBtTIamsVCNJmbzy7mwfRoIqE/edit?usp=sharing
-
New Feature: Enhanced Event Creation with Google Maps Integration
When creating a sports event, you can now choose the location using a Google Maps picker. This integration allows for a more precise location selection and automatically fills the event's location field. Additionally, you can view the map of the University of Los Andes, which displays the names of various places within the campus. This feature provides you with the option to either manually enter the location or select it directly from the map, making event creation more convenient and user-friendly.
-
Eventual Connectivity Strategies:
- For eventual connectivity within the map location feature, a Network Falling Back to Cache strategy is used. This means that before displaying the map, the user is notified that the map will be shown in cached mode, and it will only allow them to view it, but won't let them perform any actions. On the other hand, for the university map, a Cache Falling Back to Network approach is employed. After the map is initially downloaded, the map will be able to be accessed via its cached copy.
-
Caching Strategies:
- For the image and map loading, caching strategies were introduced. The image uses CachedNetworkImage and the map uses its own caching mechanism that the google maps library provides.
-
Local Storage Strategies:
- For handling local storage, Shared Preferences were utilized. Since the Google Maps map has personalized configurations and customizations, a form is presented to the user to input these data. To avoid the need for re-entering the information, it is saved in Shared Preferences. The next time the user wants to display the map, these saved options will be used.
-
Multithreading Strategies:
- The showPicker function uses a Future return type and await keywords to handle asynchronous tasks. It navigates to a settings screen, waits for the user's input, performs a connection check, and if necessary, navigates to a place picker screen.
-
Business Questions:
- What is the relationship between gender and the sports practiced by students at the University of Los Andes? (Type 4)
- What are the least utilized features in the FitConnect application, with the possibility of considering their removal? (Type 3)
{width="500"}
- Auth features and views (login/signup)
- Crashlytics and performance analysis feature (BQ type 1).
- MVVM implementation
- Singleton implementation
- Models, DTO and repository implementation
- Functionality that uses external services (different from authentication): Crashlytics and performance analysis.
- Assigned issues
- Merge issues
- Eventual connectivity strategies in sign up and login views.
- Home view/feature
- Stats view/feature (uses multithreading)
- Settings view/feature (uses shared preferences files)
- Business Question: BQ type 2: How many times has an event creator failed to bring the number of people he signed up for the activity?
-
Event create view and feature: This feature has its own eventual connectivity strategy, uses local storage and caching structures for performance and memory use. Its EC strategy is Network Only, however, user can save its changes to be manually loaded and uploaded later when internet connection is restored. This is possible because user can choose to save its data on a shared preferences file which will be used when the user wants to user that information. When there is no internet connection and the user tries to create a new event, the app will suggest the user to save the data for later.
-
Local storage use on new features: Shared preferences file for offline event conneciton.
-
Caching structures: SparseArray to store restored event after getting it from SharedPreferences file
-
Multithreading strategy: A task is executed on an executor service after receiving the data from the task created by firebase. Changes on data are view through a MutableLiveData variable.
-
Eventual connectivity strategies implemented: Network only for event create feature and for view most played sports (BQ2).
-
Bussiness Question type 2: Show the most played sports during the week. A new graphic is added to the statistics view
{width="500"}
- Design pattern implemented: Observer (BrightnessController, BrightnessObserver)
- View implemented: ProfileActivity (activity_profile.xml)
- ViewModels implemented: PhotoViewModel, UserViewModel
- Repositories implemented: PhotoRepository, UserRepository
- Context aware functionality: Change the brightness of the screen depending on the brightness.
- Functionalities using sensors: Taking a photo to change the user picture. Change the brightness of the screen depending on the brightness
- Functionality that uses external services (different from authentication): Uploading user photo to Firebase Storage.
- Merge requests
- List events
- Recommend events
- Explore view
- Assigned issues
- Merge issues
- BQ implemented (Type 2): Recommend an event to a user based on past attended events. A recommendation algorithm based on collaborative filtering was implemented, where a score is calculated for each event based on previous events to which the user has registered and the event with the highest score is recommended.
Features implemented:
- Join an event
- Download an image of the user event distribution
- Assigned issues
- Merge issues
Views implemented:
- Join an event view
- User events distribution view
- BQ implemented (Type 2): Show in a dedicated view, the distribution of events in which the user has participated in the last 30 days. It shows a pie chart and the number of events attended by type of event.
{width="500"}
- Concurrency: To save the image of event distributions to local storage, I use AsincTask, so that this operation is executed by a worker thread.
{width="500"}
- Local storage: To save the image of event distributions to local storage, within the EventsDistributionActivity class, a local storage strategy is used using app-specific files. This strategy allows to save the generated image in the external storage of the device in a safe and private way. The SaveImageTask class takes care of creating a file in the public images directory, compressing it, and saving the image bitmap to that file. Subsequently, the file is scanned to make it available in the device's gallery. This ensures that the image is stored persistently and can be accessed from other places in the application or shared with other users.
-
Eventual connectivity:
- Join feature: This follows the eventual connectivity strategy of "Network only", so if the user tries to enter an event when he has no connection, an ad will be shown to the user indicating that he cannot perform the action
- Download user event distribution image: This follows the eventual connectivity strategy called "Cached on network response", so that the information about the events that the user has attended is obtained from the cache, once they have already been brought from the network.