Commit efa46293 authored by Jayasekara T.K.K.'s avatar Jayasekara T.K.K.

Adding frontend to the repository

parent bb15ab21
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
version:
revision: 9944297138845a94256f1cf37beb88ff9a8e811a
channel: stable
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
- platform: android
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
- platform: ios
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
- platform: linux
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
- platform: macos
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
- platform: web
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
- platform: windows
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
{
"cmake.sourceDirectory": "E:/Projetcs/Flutter/Sea Cucumber/sea_cense/linux",
"workbench.colorCustomizations": {
"activityBar.background": "#2F294C",
"titleBar.activeBackground": "#41396A",
"titleBar.activeForeground": "#FCFCFD"
},
"cmake.configureOnOpen": false
}
\ No newline at end of file
# Sea Cense – A Commercially Valued Sea Cucumber Analyzer
Flutter Channel stable -- 3.10.4
## Abstract
In Sri Lanka, sea cucumbers are a highly sought-after marine product that is traded abroad. It has gained significant attention in recent years due to its high demand in the seafood market and potential medicinal properties. For the seafood industry and researchers, it is still difficult to analyze sea cucumbers accurately and effectively. This study offers an innovative solution which is of high economic value “Sea Cense” that automates the evaluation and classification of sea cucumbers utilizing advanced computer vision and machine learning approaches. The proposed analyzer incorporates a multi-step approach, including image preprocessing, feature extraction, and classification. A set of preprocessed images are used to extract morphological, textural, and color-based distinguishing characteristics. These characteristics are then used to accurately classify sea cucumber species and grade them according to quality criteria using a machine learning algorithm. This project introduces a novel approach to analyze sea cucumber length to calculate the price using a mobile application, making the task more efficient and accessible. The proposed solution “Sea Cense” comprises of 2 models that utilizes CNN algorithm.
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.sea_cense"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.sea_cense">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.sea_cense">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<application android:label="sea_cense" android:name="${applicationName}" android:icon="@mipmap/ic_launcher">
<activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data android:name="flutterEmbedding" android:value="2" />
</application>
</manifest>
package com.example.sea_cense
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.sea_cense">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
buildscript {
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
</dict>
</plist>
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Sea Cense</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>sea_cense</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>
#import "GeneratedPluginRegistrant.h"
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:provider/provider.dart';
import 'package:sea_cense/utils/navigation_service.dart';
import 'package:sea_cense/viewmodels/cucumber_viewmodel.dart';
import 'package:sea_cense/views/onboarding/splash_view.dart';
void main() {
runApp(MultiProvider(
providers: [
ChangeNotifierProvider(
create: ((context) => CucumberViewModel()),
),
],
child: const MyApp(),
));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
navigatorKey: NavigationService.navigatorKey,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const SplashView(),
builder: EasyLoading.init(),
);
}
}
class BaseAPIResponse {
BaseAPIResponse({this.status, this.data, required this.error});
bool error;
int? status;
dynamic data;
}
class CucumberJuvenile {
String? type;
String? weight;
String? initSize;
String? growthRate;
String? survivalRate;
String? totalBiomass;
CucumberJuvenile({
this.type,
this.weight,
this.initSize,
this.growthRate,
this.survivalRate,
this.totalBiomass,
});
CucumberJuvenile.fromJson(Map<String, dynamic> json) {
type = json['class'];
weight = json['finalWeight'];
initSize = json['initSizeGroup'];
survivalRate = json['survivalRate'];
totalBiomass = json['totalBioMass'];
growthRate = json['growthRatePerDay'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['class'] = type;
data['finalWeight'] = weight;
data['initSizeGroup'] = initSize;
data['survivalRate'] = survivalRate;
data['totalBioMass'] = totalBiomass;
data['growthRatePerDay'] = growthRate;
return data;
}
}
class CucumberLive {
String? type;
String? diet;
String? family;
String? phylum;
String? kingdom;
String? description;
String? scientificName;
String? conservationStatus;
CucumberLive({
this.type,
this.diet,
this.family,
this.phylum,
this.kingdom,
this.description,
this.scientificName,
this.conservationStatus,
});
CucumberLive.fromJson(Map<String, dynamic> json) {
type = json['type'];
diet = json['diet'];
family = json['family'];
phylum = json['phylum'];
kingdom = json['kingdom'];
description = json['description'];
scientificName = json['scientificName'];
conservationStatus = json['conservationStatus'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['type'] = type;
data['diet'] = diet;
data['family'] = family;
data['phylum'] = phylum;
data['kingdom'] = kingdom;
data['description'] = description;
data['scientificName'] = scientificName;
data['conservationStatus'] = conservationStatus;
return data;
}
}
class CucumberPrice {
String? width;
String? price;
String? length;
String? category;
CucumberPrice({
this.price,
this.width,
this.length,
this.category,
});
CucumberPrice.fromJson(Map<String, dynamic> json) {
width = json['width'];
price = json['price'];
length = json['length'];
category = json['category'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['width'] = width;
data['price'] = price;
data['length'] = length;
data['category'] = category;
return data;
}
}
class CucumberProcessed {
String? predictedType;
String? predictedClass;
String? predictedProbabilities;
CucumberProcessed({
this.predictedType,
this.predictedClass,
this.predictedProbabilities,
});
CucumberProcessed.fromJson(Map<String, dynamic> json) {
predictedClass = json['quality'];
predictedType = json['predicted_name'];
predictedProbabilities = json['predicted_probabilities'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['quality'] = predictedClass;
data['predicted_name'] = predictedType;
data['predicted_probabilities'] = predictedProbabilities;
return data;
}
}
class User {
String? username;
String? password;
User({
this.username,
this.password,
});
User.fromJson(Map<String, dynamic> json) {
username = json['username'];
password = json['password'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['username'] = username;
data['password'] = password;
return data;
}
}
import 'package:dio/dio.dart';
import 'package:sea_cense/models/base_api_response.dart';
class Network {
static Future<BaseAPIResponse> upload(
{required String filePath,
required String fileName,
required String endpoint,
required Function(int, int)? onSendProgress}) async {
FormData data = FormData.fromMap({
"image_path": await MultipartFile.fromFile(
filePath,
filename: fileName,
),
});
Dio dio = Dio();
dio.options.connectTimeout = const Duration(seconds: 20);
try {
var response = await dio.post(
endpoint,
data: data,
onSendProgress: onSendProgress,
);
return BaseAPIResponse(data: response.data, error: false, status: response.statusCode);
} on DioException catch (e) {
return BaseAPIResponse(data: null, error: true, status: e.response?.statusCode);
}
}
}
import 'package:image_picker/image_picker.dart';
import 'package:sea_cense/models/base_api_response.dart';
import 'package:sea_cense/network/network.dart';
class CucumberService {
Future<BaseAPIResponse> uploadImage(
XFile imageFile, Function(int, int)? onSendProgress, String endpoint) async {
BaseAPIResponse response = await Network.upload(
filePath: imageFile.path,
fileName: imageFile.name,
endpoint: endpoint,
onSendProgress: onSendProgress);
return response;
}
}
import 'package:flutter/material.dart';
const defaultColor = Color(0xFF2D8CF0);
const inactiveColor = Color(0xFFCFCFCF);
const warningAlertColor = Color(0xFFFF7F50);
const successAlertColor = Color(0xFF009688);
const errorAlertColor = Color(0xFFFF4757);
const loadingPlaceholderColor = Color(0xFFE0E0E0);
//Fonts and text
const labelStyle = TextStyle(color: Colors.black26, fontWeight: FontWeight.w500, fontSize: 15.0);
const hintStyle = TextStyle(color: Colors.black26, fontWeight: FontWeight.w500, fontSize: 13.0);
const topicStyle = TextStyle(color: Colors.black87, fontWeight: FontWeight.w500, fontSize: 25.0);
import 'package:image_picker/image_picker.dart';
class CameraHelper {
static Future<XFile> selectImages() async {
final XFile? imageFile;
final ImagePicker picker = ImagePicker();
imageFile = await picker.pickImage(imageQuality: 85, source: ImageSource.gallery);
return imageFile!;
}
static Future<XFile> takeImages() async {
final XFile? imageFile;
final ImagePicker picker = ImagePicker();
imageFile = await picker.pickImage(imageQuality: 85, source: ImageSource.camera);
return imageFile!;
}
}
enum Loading { initial, more, finished }
enum ProcessorType { live, processed, price, juvenile }
enum RequestType { get, post, put, patch, delete }
import 'package:sea_cense/models/cucumber_live.dart';
import 'package:sea_cense/models/cucumber_price.dart';
List<CucumberLive> seaCucumbers = [
CucumberLive(
type: 'H.Scabra',
description:
'Holothuria scabra, or sandfish, is a species of sea cucumber in the family Holothuriidae. It was placed in the subgenus Metriatyla by Rowe in 1969 and is the CucumberLive (type species of the subgenus). Sandfish are harvested and processed into "beche-de-mer" and eaten in China and other Pacific coastal communities',
scientificName: 'Holothuria scabra',
conservationStatus: 'Endangered (Population decreasing) Encyclopedia of Life',
family: 'Holothuriidae',
kingdom: 'Animalia',
phylum: 'Echinoderma',
diet: 'Diatoms and seaweeds'),
CucumberLive(
type: 'H.Spinifera',
description:
'Holothuria spinifera, the brown sandfish, is a species of sea cucumber in the family Holothuriidae. It is placed in the subgenus Theelothuria, making its full name Holothuria (Theelothuria) spinifera. In India it is known as cheena attai or raja attai. It lives in tropical regions of the west Indo-Pacific Ocean at depths ranging from 32 to 60 metres (105 to 197 ft). It is fished commercially to produce beche-de-mer.',
scientificName: 'Holothuria spinifera',
conservationStatus: '--',
family: 'Holothuroidea',
kingdom: 'Animalia',
phylum: 'Echinodermata',
diet: '--'),
CucumberLive(
type: 'B.Vitiensis',
description:
'Bohadschia vitiensis is a species of sea cucumber in the family Holothuriidae. It is also known as the brown sandfish and brown sea cucumber. It is widespread in shallow waters of the Indo-Pacific. It appears to be able to hybridize with Bohadschia argus. Bohadschia vitiensis can grow to 50 cm in total length.',
scientificName: 'Bohadschia vitiensis',
conservationStatus: '--',
family: 'Holothuroidea',
kingdom: 'Animalia',
phylum: 'Echinodermata',
diet: '--'),
CucumberLive(
type: 'S.Naso',
description:
'It is a classical Stichopus, stout and trapezoidal to rectangular in cross-section and with three rows of podia on the ventral face. Its color is uniform or mottled, from sandy to darj brown, with black lines and dots. Its dorsum wears huge and erected (though retractile) tubercle-like excrescences.Stichopus naso, or also known as tropical holothurian but generally, in modern terms, it is considered a sea cucumber. Stichopus naso was discovered in 1867 specifically in the Philippines. The most recent discovery of this species was in 2011 by the coast of Kagoshima, Kyushu, Japan',
scientificName: 'Stichopus naso',
conservationStatus: '--',
family: 'Holothuroidea',
kingdom: 'Animalia',
phylum: 'Echinodermata',
diet: '--'),
CucumberLive(
type: 'Unknown',
description: '--',
scientificName: '--',
conservationStatus: '--',
family: '--',
kingdom: '--',
phylum: '--',
diet: '--'),
];
Map<String, CucumberPrice> priceCatergories = {
'img_cat1': CucumberPrice(category: 'img_cat1', price: '600-650', length: '15-20', width: '350-450'),
'img_cat2': CucumberPrice(category: 'img_cat2', price: '800-900', length: '21-25', width: '451-600'),
'img_cat3': CucumberPrice(category: 'img_cat3', price: '1100-1300', length: '15-20', width: '601-750'),
'Unknown': CucumberPrice(category: 'Unknown', price: '--', length: '--', width: '--'),
};
import 'package:flutter/material.dart';
class NavigationService {
static GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
}
import 'package:shared_preferences/shared_preferences.dart';
class SharedPreference {
static setUser(String user) async {
final SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
sharedPrefs.setString("user_info", user);
}
static Future<String?> getUser() async {
final SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
return sharedPrefs.getString("user_info");
}
static clear() async {
final SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
sharedPrefs.clear();
sharedPrefs.reload();
}
}
class UrlConstants {
// static const String environment = "development";
static const String environment = "production";
static const String baseUrl = "http://10.0.2.2:5000";
static String getLiveEndpoint() {
if (environment == "development") {
return "$baseUrl/type-identification";
} else {
return "https://identifytype-w2qyntknzq-uc.a.run.app/type-identification";
}
}
static String getProcessedEndpoint() {
if (environment == "development") {
return "$baseUrl/quality";
} else {
return "https://qualityprediction-w2qyntknzq-uc.a.run.app/quality";
}
}
static String getPriceEndpoint() {
if (environment == "development") {
return "$baseUrl/price-prediction";
} else {
return "https://predictprice-w2qyntknzq-uc.a.run.app/price-prediction";
}
}
static String getJuvenileEndpoint() {
if (environment == "development") {
return "$baseUrl/age-prediction";
} else {
return "https://predictage-w2qyntknzq-uc.a.run.app/age-prediction";
}
}
}
import 'package:flutter/material.dart';
class Utils {
static void showSnackBar(String message, BuildContext context) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
}
}
This diff is collapsed.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:sea_cense/models/user.dart';
import 'package:sea_cense/utils/navigation_service.dart';
import 'package:sea_cense/utils/storage.dart';
import 'package:sea_cense/utils/utils.dart';
class SignInUpViewModel extends ChangeNotifier {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
final TextEditingController usernameController = TextEditingController();
final TextEditingController passwordCntroller = TextEditingController();
final TextEditingController confPasswordCntroller = TextEditingController();
List<User> userList = [];
void signin({required VoidCallback onSuccess}) async {
String? users = await SharedPreference.getUser();
userList = List<User>.from(json.decode(users!).map((userJson) => User.fromJson(userJson)));
User user = User();
user.username = usernameController.text;
user.password = passwordCntroller.text;
if (userList.isNotEmpty) {
User? foundUser = userList.firstWhere(
(value) => value.username!.toLowerCase() == user.username!.toLowerCase(),
orElse: () => User(),
);
if (foundUser.username == "" || foundUser.username == null) {
Utils.showSnackBar('User does not exist', NavigationService.navigatorKey.currentContext!);
} else {
if (foundUser.username!.toLowerCase() == user.username!.toLowerCase() &&
foundUser.password == user.password) {
onSuccess();
} else {
Utils.showSnackBar('Incorrect password', NavigationService.navigatorKey.currentContext!);
}
}
} else {
Utils.showSnackBar('User does not exist', NavigationService.navigatorKey.currentContext!);
}
}
void signup({required VoidCallback onSuccess}) async {
String? users = await SharedPreference.getUser();
if (users == null || users == '') {
} else {
userList = List<User>.from(json.decode(users).map((userJson) => User.fromJson(userJson)));
}
if (usernameController.text.isEmpty ||
passwordCntroller.text.isEmpty ||
confPasswordCntroller.text.isEmpty) {
Utils.showSnackBar('Fields cannot be empty', NavigationService.navigatorKey.currentContext!);
return;
}
if (passwordCntroller.text == confPasswordCntroller.text) {
User user = User();
user.username = usernameController.text;
user.password = passwordCntroller.text;
if (userList.isNotEmpty) {
User? foundUser = userList.firstWhere(
(value) => value.username!.toLowerCase() == user.username!.toLowerCase(),
orElse: () => User(),
);
if (foundUser.username == "" || foundUser.username == null) {
userList.add(user);
SharedPreference.setUser(jsonEncode(userList));
onSuccess();
} else {
Utils.showSnackBar('User already exists', NavigationService.navigatorKey.currentContext!);
}
} else {
userList.add(user);
SharedPreference.setUser(jsonEncode(userList));
onSuccess();
}
} else {
Utils.showSnackBar('Passwords do not match', NavigationService.navigatorKey.currentContext!);
}
}
disposeControllers() {
usernameController.dispose();
passwordCntroller.dispose();
confPasswordCntroller.dispose();
userList.clear();
}
}
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:sea_cense/style.dart';
import 'package:sea_cense/utils/enums/processor_type.dart';
import 'package:sea_cense/viewmodels/cucumber_viewmodel.dart';
import 'package:sea_cense/views/onboarding/onboarding.dart';
import 'package:sea_cense/widgets/home_tile_widget.dart';
import 'package:toggle_switch/toggle_switch.dart';
class HomeView extends StatefulWidget {
const HomeView({super.key});
@override
State<HomeView> createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
body: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Consumer<CucumberViewModel>(builder: (context, model, child) {
return Column(
children: [
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height / 3.5,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/home_bg.png"),
alignment: Alignment.topCenter,
fit: BoxFit.fitWidth,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'assets/images/logo_without_text.png',
height: 100,
),
const SizedBox(
width: 20,
),
const Text(
'Sea Sence',
style: TextStyle(color: Colors.white, fontSize: 30, fontWeight: FontWeight.w200),
)
],
),
),
ToggleSwitch(
initialLabelIndex: 0,
totalSwitches: 2,
activeBgColor: const [defaultColor],
inactiveBgColor: Colors.black26,
labels: const [
'Camera',
'Gallery',
],
onToggle: (index) {
index == 0 ? model.isCamera = true : model.isCamera = false;
},
),
Row(
children: [
HomeTileWidget(
icon: Icons.waves,
title: 'Live',
onTap: () {
model.addImage(context, ProcessorType.live);
},
),
HomeTileWidget(
icon: Icons.dashboard,
title: 'Processed',
onTap: () {
model.addImage(context, ProcessorType.processed);
},
),
],
),
Row(
children: [
HomeTileWidget(
icon: Icons.price_check,
title: 'Price',
onTap: () {
model.addImage(context, ProcessorType.price);
},
),
HomeTileWidget(
icon: Icons.new_label,
title: 'Juvenile',
onTap: () {
model.addImage(context, ProcessorType.juvenile);
},
),
],
),
InkWell(
onTap: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => const OnboardingView()));
},
child: Container(
width: MediaQuery.of(context).size.width / 3,
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 20),
margin: const EdgeInsets.symmetric(vertical: 30),
decoration: BoxDecoration(
border: Border.all(
width: 1.0,
color: const Color(0xFFBE2929),
),
borderRadius: BorderRadius.circular(10)),
child: const Row(
children: [
Icon(
Icons.logout,
color: Color(0xFFBE2929),
),
SizedBox(
width: 20,
),
Text(
'Logout',
style: TextStyle(fontWeight: FontWeight.w500),
)
],
),
),
)
],
);
}),
),
),
);
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment