diff --git a/.idea/compiler.xml b/.idea/compiler.xml index b86273d9424b73937ca6ad7933f6b7eeb60f4a3d..b589d56e9f285d8cfdc6c270853a5d439021a278 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="CompilerConfiguration"> - <bytecodeTargetLevel target="21" /> + <bytecodeTargetLevel target="17" /> </component> </project> \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000000000000000000000000000000000000..2b4faaa151cf07bc638245989c239f22429b6ff1 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="deploymentTargetSelector"> + <selectionStates> + <SelectionState runConfigName="app"> + <option name="selectionMode" value="DROPDOWN" /> + <DropdownSelection timestamp="2025-01-07T01:25:25.467972600Z"> + <Target type="DEFAULT_BOOT"> + <handle> + <DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\Prosp\.android\avd\Pixel_8a_API_35.avd" /> + </handle> + </Target> + </DropdownSelection> + <DialogSelection /> + </SelectionState> + </selectionStates> + </component> +</project> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index b2c751a35c77d4f7b713fe3bfc2cfb693e4d984f..0ad17cbd33a2f389d524bc4bfef9c52e1f7ab490 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ExternalStorageConfigurationManager" enabled="true" /> - <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/build/classes" /> </component> <component name="ProjectType"> diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3cef3fdeb70e696e4bebaa6df4e6485448e1d1b7..41dbf8598cbdca8cdbec2adf7d88fb2d77ea9e51 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,7 +38,7 @@ android:name=".HomeActivity" android:screenOrientation="portrait" /> <activity - android:name=".TutorialActivity" + android:name=".tutorials.TutorialActivity" android:screenOrientation="portrait" /> <activity android:name=".PublicActivity" diff --git a/app/src/main/java/com/example/myapplication/HomeActivity.java b/app/src/main/java/com/example/myapplication/HomeActivity.java index 8bef76f8915a35f73ba547e15bd381f253907de9..976416a8435208ece3fdbf0b589354900f5bb917 100644 --- a/app/src/main/java/com/example/myapplication/HomeActivity.java +++ b/app/src/main/java/com/example/myapplication/HomeActivity.java @@ -9,31 +9,62 @@ import android.widget.Button; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatDelegate; +import com.example.myapplication.tutorials.TutorialActivity; + +/** + * HomeActivity represents the main screen of the application where users can access + * various sections of the app, such as Learning, Stats, Settings, and Photo. The activity + * also handles displaying tutorials for first-time users or specific navigation flows. + */ public class HomeActivity extends AppCompatActivity { + + /** + * Called when the activity is resumed. It checks if a tutorial should be shown based + * on the user's previous interactions or the source of the current intent (e.g., from the photo page). + * If the tutorial is needed, it is triggered. + */ @Override protected void onResume() { super.onResume(); - // If tutorial ran from photo page + // If tutorial ran from photo page, show the tutorial boolean fromPhotoPage = getIntent().getBooleanExtra("FROM_PHOTO_PAGE", false); if (fromPhotoPage) runTutorial(true); String tutorialKey = "FIRST_TIME_TUTORIAL"; - boolean preferences = getIntent().getBooleanExtra("PREFERENCES",true); + boolean preferences = getIntent().getBooleanExtra("PREFERENCES", true); + + // If preferences is false, skip the tutorial on the first time if (!preferences) getPreferences(MODE_PRIVATE).edit().putBoolean(tutorialKey, false).apply(); + boolean firstTime = getPreferences(MODE_PRIVATE).getBoolean(tutorialKey, true); + + // Run the tutorial if it's the first time using the app if (firstTime) { runTutorial(false); } } + /** + * Starts the TutorialActivity to display a tutorial. + * It checks if the tutorial is to be shown due to navigating from the photo page. + * + * @param fromPhotoPage A boolean that specifies whether the tutorial is shown due to navigation from the photo page. + */ private void runTutorial(boolean fromPhotoPage) { - Intent intent = new Intent(HomeActivity.this, TutorialActivity.class); - intent.putExtra("FROM_PHOTO_PAGE", fromPhotoPage); - startActivity(intent); - finish(); + Intent intent = new Intent(HomeActivity.this, TutorialActivity.class); + intent.putExtra("FROM_PHOTO_PAGE", fromPhotoPage); + startActivity(intent); + finish(); } + /** + * Called when the activity is created. This method sets up the theme based on user preferences, + * initializes the UI, and sets click listeners for each button to navigate to different pages. + * + * @param savedInstanceState A bundle containing the saved state of the activity, + * or null if there was no previously saved state. + */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -45,12 +76,10 @@ public class HomeActivity extends AppCompatActivity { setContentView(R.layout.activity_home); - /* - On click listener for the Learning Button to open the "Apprendre" page by clicking - on the "Apprendre" button on the home page - */ - Button learningButton=(Button)findViewById(R.id.learning_button); - View.OnClickListener listenerLearningButton=new View.OnClickListener() { + // On click listener for the Learning Button to open the "Apprendre" page by clicking + // on the "Apprendre" button on the home page + Button learningButton = (Button) findViewById(R.id.learning_button); + View.OnClickListener listenerLearningButton = new View.OnClickListener() { @Override public void onClick(View view) { goToPage("LearningPage"); @@ -58,12 +87,10 @@ public class HomeActivity extends AppCompatActivity { }; learningButton.setOnClickListener(listenerLearningButton); - /* - On click listener for the Stats Button to open the "Statistiques" page by clicking - on the "Statistiques" button on the home page - */ - Button statsButton=(Button)findViewById(R.id.stats_button); - View.OnClickListener listenerStatsButton=new View.OnClickListener() { + // On click listener for the Stats Button to open the "Statistiques" page by clicking + // on the "Statistiques" button on the home page + Button statsButton = (Button) findViewById(R.id.stats_button); + View.OnClickListener listenerStatsButton = new View.OnClickListener() { @Override public void onClick(View view) { goToPage("StatsPage"); @@ -71,12 +98,10 @@ public class HomeActivity extends AppCompatActivity { }; statsButton.setOnClickListener(listenerStatsButton); - /* - On click listener for the Settings Button to open the "Paramètres" page by clicking - on the "Paramètres" button on the home page - */ - Button settingsButton=(Button)findViewById(R.id.settings_button); - View.OnClickListener listenerSettingsButton=new View.OnClickListener() { + // On click listener for the Settings Button to open the "Paramètres" page by clicking + // on the "Paramètres" button on the home page + Button settingsButton = (Button) findViewById(R.id.settings_button); + View.OnClickListener listenerSettingsButton = new View.OnClickListener() { @Override public void onClick(View view) { goToPage("SettingsPage"); @@ -84,12 +109,10 @@ public class HomeActivity extends AppCompatActivity { }; settingsButton.setOnClickListener(listenerSettingsButton); - /* - On click listener for the Photo Button to open the "Documenter" page by clicking - on the "Documenter" button on the home page - */ - Button photoButton=(Button)findViewById(R.id.photo_button); - View.OnClickListener listenerPhotoButton=new View.OnClickListener() { + // On click listener for the Photo Button to open the "Documenter" page by clicking + // on the "Documenter" button on the home page + Button photoButton = (Button) findViewById(R.id.photo_button); + View.OnClickListener listenerPhotoButton = new View.OnClickListener() { @Override public void onClick(View view) { goToPage("PhotoPage"); @@ -97,9 +120,12 @@ public class HomeActivity extends AppCompatActivity { }; photoButton.setOnClickListener(listenerPhotoButton); } + /** - * A method to go to the learning page - * @param pageToGo a string to signify to which page we want to go + * Navigates the user to the specified page by opening the PublicActivity + * and passing the target fragment as an extra. + * + * @param pageToGo A string representing the target page (fragment) to navigate to. */ public void goToPage(String pageToGo) { Intent intent = new Intent(HomeActivity.this, PublicActivity.class); diff --git a/app/src/main/java/com/example/myapplication/LearningPage.java b/app/src/main/java/com/example/myapplication/LearningPage.java index de979da49d19f8b22a1e4a2ee744be470caa0dd1..a1f490c6205f00116c1f4136c28a1aaa0a4014e3 100644 --- a/app/src/main/java/com/example/myapplication/LearningPage.java +++ b/app/src/main/java/com/example/myapplication/LearningPage.java @@ -11,7 +11,30 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Button; +import com.example.myapplication.overlays.OverlayDialogFragmentBecasseau; +import com.example.myapplication.overlays.OverlayDialogFragmentBernache; +import com.example.myapplication.overlays.OverlayDialogFragmentChevalier; +import com.example.myapplication.overlays.OverlayDialogFragmentCormoran; +import com.example.myapplication.overlays.OverlayDialogFragmentFoulque; +import com.example.myapplication.overlays.OverlayDialogFragmentGoeland; +import com.example.myapplication.overlays.OverlayDialogFragmentMouette; +import com.example.myapplication.overlays.OverlayDialogFragmentPluvier; +import com.example.myapplication.overlays.OverlayDialogFragmentTadorne; + +/** + * This fragment represents a learning page where the user can interact with different buttons. + * Each button triggers a dialog showing detailed information about a particular bird species. + */ public class LearningPage extends Fragment { + + /** + * Inflates the layout for this fragment. + * + * @param inflater The LayoutInflater object that can be used to inflate views in the fragment. + * @param container The parent container that the fragment's UI should be attached to. + * @param savedInstanceState A bundle containing the state of the fragment. + * @return The root view of the fragment's layout. + */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -19,158 +42,176 @@ public class LearningPage extends Fragment { return inflater.inflate(R.layout.fragment_learning_page, container, false); } + /** + * Sets up the listeners for each button on the learning page. Each listener triggers the display + * of a corresponding overlay dialog fragment for a bird species. + * + * @param view The root view of the fragment. + * @param savedInstanceState A bundle containing the state of the fragment. + */ @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - // Listener pour le becasseau + + // Set up the listener for the Becasseau button Button buttonBecasseau = (Button) this.getView().findViewById(R.id.button_becasseau); - View.OnClickListener listenerButtonBecasseau=new View.OnClickListener() { + View.OnClickListener listenerButtonBecasseau = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayBecasseau(); } }; - buttonBecasseau.setOnClickListener(listenerButtonBecasseau); - // Listener pour la bernache + // Set up the listener for the Bernache button Button buttonBernache = (Button) this.getView().findViewById(R.id.button_bernache); - - View.OnClickListener listenerButtonBernache=new View.OnClickListener() { + View.OnClickListener listenerButtonBernache = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayBernache(); } }; - buttonBernache.setOnClickListener(listenerButtonBernache); - // Listener pour le goeland + // Set up the listener for the Goeland button Button buttonGoeland = (Button) this.getView().findViewById(R.id.button_goeland); - - View.OnClickListener listenerButtonGoeland=new View.OnClickListener() { + View.OnClickListener listenerButtonGoeland = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayGoeland(); } }; - buttonGoeland.setOnClickListener(listenerButtonGoeland); - // Listener pour la mouette + // Set up the listener for the Mouette button Button buttonMouette = (Button) this.getView().findViewById(R.id.button_mouette); - - View.OnClickListener listenerButtonMouette=new View.OnClickListener() { + View.OnClickListener listenerButtonMouette = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayMouette(); } }; - buttonMouette.setOnClickListener(listenerButtonMouette); - // Listener pour le pluvier + // Set up the listener for the Pluvier button Button buttonPluvier = (Button) this.getView().findViewById(R.id.button_pluvier); - - View.OnClickListener listenerButtonPluvier=new View.OnClickListener() { + View.OnClickListener listenerButtonPluvier = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayPluvier(); } }; - buttonPluvier.setOnClickListener(listenerButtonPluvier); - // Listener pour le cormoran + // Set up the listener for the Cormoran button Button buttonCormoran = (Button) this.getView().findViewById(R.id.button_cormoran); - - View.OnClickListener listenerButtonCormoran=new View.OnClickListener() { + View.OnClickListener listenerButtonCormoran = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayCormoran(); } }; - buttonCormoran.setOnClickListener(listenerButtonCormoran); - // Listener pour la foulque macroule + // Set up the listener for the Foulque button Button buttonFoulque = (Button) this.getView().findViewById(R.id.button_foulque); - - View.OnClickListener listenerButtonFoulque=new View.OnClickListener() { + View.OnClickListener listenerButtonFoulque = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayFoulque(); } }; - buttonFoulque.setOnClickListener(listenerButtonFoulque); - // Listener pour le tadorne de Belon + // Set up the listener for the Tadorne button Button buttonTadorne = (Button) this.getView().findViewById(R.id.button_tadorne); - - View.OnClickListener listenerButtonTadorne=new View.OnClickListener() { + View.OnClickListener listenerButtonTadorne = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayTadorne(); } }; - buttonTadorne.setOnClickListener(listenerButtonTadorne); - // Listener pour le chevalier gambette + // Set up the listener for the Chevalier button Button buttonChevalier = (Button) this.getView().findViewById(R.id.button_chevalier); - - View.OnClickListener listenerButtonChevalier=new View.OnClickListener() { + View.OnClickListener listenerButtonChevalier = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayChevalier(); } }; - buttonChevalier.setOnClickListener(listenerButtonChevalier); } + /** + * Shows an overlay dialog for the Becasseau bird species. + */ public void showOverlayBecasseau() { OverlayDialogFragmentBecasseau overlayDialogBecasseau = new OverlayDialogFragmentBecasseau(); overlayDialogBecasseau.show(getParentFragmentManager(), "overlayDialogBecasseau"); } + /** + * Shows an overlay dialog for the Bernache bird species. + */ public void showOverlayBernache() { OverlayDialogFragmentBernache overlayDialogBernache = new OverlayDialogFragmentBernache(); overlayDialogBernache.show(getParentFragmentManager(), "overlayDialogBernache"); } + /** + * Shows an overlay dialog for the Goeland bird species. + */ public void showOverlayGoeland() { OverlayDialogFragmentGoeland overlayDialogGoeland = new OverlayDialogFragmentGoeland(); overlayDialogGoeland.show(getParentFragmentManager(), "overlayDialogGoeland"); } + /** + * Shows an overlay dialog for the Mouette bird species. + */ public void showOverlayMouette() { OverlayDialogFragmentMouette overlayDialogMouette = new OverlayDialogFragmentMouette(); overlayDialogMouette.show(getParentFragmentManager(), "overlayDialogMouette"); } + /** + * Shows an overlay dialog for the Pluvier bird species. + */ public void showOverlayPluvier() { OverlayDialogFragmentPluvier overlayDialogPluvier = new OverlayDialogFragmentPluvier(); overlayDialogPluvier.show(getParentFragmentManager(), "overlayDialogPluvier"); } + /** + * Shows an overlay dialog for the Cormoran bird species. + */ public void showOverlayCormoran() { OverlayDialogFragmentCormoran overlayDialogCormoran = new OverlayDialogFragmentCormoran(); overlayDialogCormoran.show(getParentFragmentManager(), "overlayDialogCormoran"); } + /** + * Shows an overlay dialog for the Foulque bird species. + */ public void showOverlayFoulque() { OverlayDialogFragmentFoulque overlayDialogFoulque = new OverlayDialogFragmentFoulque(); overlayDialogFoulque.show(getParentFragmentManager(), "overlayDialogFoulque"); } + /** + * Shows an overlay dialog for the Tadorne bird species. + */ public void showOverlayTadorne() { OverlayDialogFragmentTadorne overlayDialogTadorne = new OverlayDialogFragmentTadorne(); overlayDialogTadorne.show(getParentFragmentManager(), "overlayDialogTadorne"); } + /** + * Shows an overlay dialog for the Chevalier bird species. + */ public void showOverlayChevalier() { OverlayDialogFragmentChevalier overlayDialogChevalier = new OverlayDialogFragmentChevalier(); overlayDialogChevalier.show(getParentFragmentManager(), "overlayDialogChevalier"); } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/myapplication/MainActivity.java b/app/src/main/java/com/example/myapplication/MainActivity.java index 12f9d387168dae293843e2a41b081a1f50e9c40d..3ff2cfb7a8d9b2d5849ca2cfec3b684f2afb1fa4 100644 --- a/app/src/main/java/com/example/myapplication/MainActivity.java +++ b/app/src/main/java/com/example/myapplication/MainActivity.java @@ -2,23 +2,27 @@ package com.example.myapplication; import android.content.Intent; import android.content.SharedPreferences; -import android.graphics.Bitmap; import android.os.Bundle; import android.os.Handler; -import android.view.View; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatDelegate; -import org.tensorflow.lite.support.tensorbuffer.TensorBuffer; - -import java.io.IOException; -import java.util.List; - +/** + * The MainActivity class represents the main entry point of the application. + * It is responsible for loading the theme preference and transitioning to the HomeActivity + * after a brief delay. + */ public class MainActivity extends AppCompatActivity { + + /** + * Called when the activity is first created. This method is used to initialize + * the app's settings, including theme preferences, and to set up a delayed transition + * to the HomeActivity. + * + * @param savedInstanceState A bundle containing the saved state of the activity, + * or null if there was no previously saved state. + */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -26,64 +30,29 @@ public class MainActivity extends AppCompatActivity { // Load the saved theme mode from SharedPreferences SharedPreferences preferences = getSharedPreferences("theme_prefs", MODE_PRIVATE); int themeMode = preferences.getInt("theme_mode", AppCompatDelegate.MODE_NIGHT_NO); + + // Apply the saved theme mode to the app AppCompatDelegate.setDefaultNightMode(themeMode); + // Set the content view to the layout for this activity setContentView(R.layout.activity_main); - + // Delay time before transitioning to HomeActivity (in milliseconds) int delayMillis = 3000; + // Create a new handler to delay the transition to HomeActivity new Handler().postDelayed(new Runnable() { @Override public void run() { + // Create an intent to launch HomeActivity Intent intent = new Intent(MainActivity.this, HomeActivity.class); - startActivity(intent); - finish(); - } - }, delayMillis); - - - - } - public void testPredict(View v){ - TextView textView = findViewById(R.id.testInference); - TestModeleTflite testModeleTflite = null; - try { - testModeleTflite = new TestModeleTflite(this); - String currentDir = System.getProperty("user.dir"); - String imagePath = "mouette_rieuse.jpg"; - Bitmap inputImage = TestModeleTflite.loadImage(imagePath, this); - - if (inputImage == null) { - System.out.println("Failed to load the image. Check the file path."); - return; - } - - // Perform prediction - TensorBuffer outputBuffer = testModeleTflite.predict(inputImage); - - // Process the output to get bounding boxes - List<BoundingBox> boxes = testModeleTflite.processOutput(outputBuffer); - - if (boxes == null || boxes.isEmpty()) { - textView.setText("No objects detected in the image."); - } else { - // Output results to the console - for (BoundingBox box : boxes) { - System.out.println("Class: " + box.clsName); - System.out.println("Confidence: " + box.cnf); - System.out.println("----------------------------"); - textView.setText("Class: " + box.clsName + " Confidence : " + box.cnf); - } + // Start HomeActivity + startActivity(intent); + // Finish MainActivity so that it won't remain in the back stack + finish(); } - } catch (IOException e) { - throw new RuntimeException(e); - } - - //textView.setText("bonjour"); + }, delayMillis); // Delay the transition by the specified time } - - } diff --git a/app/src/main/java/com/example/myapplication/PhotoPage.java b/app/src/main/java/com/example/myapplication/PhotoPage.java index bd4eb1dd791782ef4c3fef5c48498ceb5bd2766c..199b9f9260f62cbed84ccda198e6765c5fb507f9 100644 --- a/app/src/main/java/com/example/myapplication/PhotoPage.java +++ b/app/src/main/java/com/example/myapplication/PhotoPage.java @@ -8,7 +8,12 @@ import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import com.example.myapplication.R; + +import com.example.myapplication.overlays.OverlayDialogFragmentForm; +import com.example.myapplication.recognation_logic.NoSpeciesRecognizedException; +import com.example.myapplication.recognation_logic.OutputPredictedImage; +import com.example.myapplication.recognation_logic.RecognizeSpecie; +import com.example.myapplication.recognation_logic.TestModeleTflite; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -24,12 +29,18 @@ import android.view.ViewGroup; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.Spinner; +import android.widget.TableLayout; +import android.widget.TableRow; import android.widget.TextView; import android.widget.Toast; import java.io.IOException; +import java.util.Map; +/** + * A Fragment class responsible for capturing photos, selecting images from the gallery, + * processing them, and displaying species recognition results. + */ public class PhotoPage extends Fragment { private static final int CAMERA_REQUEST_CODE = 3; private static final int GALLERY_REQUEST_CODE = 1; @@ -41,35 +52,49 @@ public class PhotoPage extends Fragment { TextView textPredictionOutput, textProbabilityOutput; ImageView imageView; FrameLayout formLayout; - + FrameLayout tableResultsLayout; + OutputPredictedImage outputPredictedImage; + private Map<String, RecognizeSpecie> predictionResults; + TableLayout tablePredictionResults; int imageSize = 256; + /** + * Inflates the layout and initializes UI components for the fragment. + * + * @param inflater LayoutInflater for inflating the layout. + * @param container Parent container for the fragment. + * @param savedInstanceState Saved state of the fragment. + * @return The view for the fragment. + */ @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { predictionOutput = null; View view = inflater.inflate(R.layout.fragment_photo_page, container, false); + // Initialize UI elements cameraButton = view.findViewById(R.id.camera_button); folderButton = view.findViewById(R.id.folder_button); identifiedSpeciesButton = view.findViewById(R.id.identified_species_button); - textPredictionOutput = view.findViewById(R.id.textPredictionOutput); - //textProbabilityOutput = view.findViewById(R.id.textProbabilityOutput); + tablePredictionResults = view.findViewById(R.id.results_table1); + tableResultsLayout = view.findViewById(R.id.tableResultsLayout); + tableResultsLayout.setVisibility(View.INVISIBLE); imageView = view.findViewById(R.id.main_image_display); Button seeTutoButton = view.findViewById(R.id.see_tuto); - - // Set up button click listeners cameraButton.setOnClickListener(new View.OnClickListener() { @RequiresApi(api = Build.VERSION_CODES.M) @Override public void onClick(View v) { + // Check for camera permission and request it if necessary if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { + // Start the camera activity if permission is granted Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(cameraIntent, CAMERA_REQUEST_CODE); } else { + // Request camera permission if not granted requestPermissions(new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE); } } @@ -78,23 +103,26 @@ public class PhotoPage extends Fragment { folderButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + // Open the gallery to pick an image Intent folderIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(folderIntent, GALLERY_REQUEST_CODE); - } }); identifiedSpeciesButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { + // Display the identified species or a message if no species is recognized if(predictionOutput == null){ Toast.makeText(getContext(), "Veuillez prendre une photo ou charger une image depuis votre gallerie.", Toast.LENGTH_LONG).show(); } - else Toast.makeText(getContext(), predictionOutput, Toast.LENGTH_LONG).show(); - - + else { + Toast.makeText(getContext(), predictionOutput, Toast.LENGTH_LONG).show(); + } } }); + + // Set up tutorial button View.OnClickListener lSeeTutoButton = new View.OnClickListener() { @Override public void onClick(View view) { @@ -103,8 +131,6 @@ public class PhotoPage extends Fragment { startActivity(intent); getActivity().finish(); } - - }; seeTutoButton.setOnClickListener(lSeeTutoButton); @@ -112,10 +138,18 @@ public class PhotoPage extends Fragment { return view; } + /** + * Handles the results from the camera or gallery activities. + * + * @param requestCode Code indicating the request type (camera or gallery). + * @param resultCode Result code from the activity. + * @param data Intent containing the result data. + */ @Override public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); + // Handle result based on the request code if (data == null || resultCode != getActivity().RESULT_OK) { Log.e("PhotoPage", "Error: Data is null or result not OK"); return; @@ -151,55 +185,137 @@ public class PhotoPage extends Fragment { } } + /** + * Processes the given bitmap image for species recognition. + * + * @param bitmap The image to be processed. + */ private void processImage(Bitmap bitmap) { // Resize and/or preprocess the image for your recognition program Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, imageSize, imageSize, true); - // You can pass this `scaledBitmap` to your prediction program + // You can pass this scaledBitmap to your prediction program Log.d("PhotoPage", "Bitmap processed for prediction: " + scaledBitmap.toString()); try { + // Initialize the model and get predictions TestModeleTflite testModeleTflite = new TestModeleTflite(this.getContext()); - OutputPredictedImage outputPredictedImage = testModeleTflite.recognizeSpeciesClass(bitmap); + outputPredictedImage = testModeleTflite.recognizeSpeciesClass(bitmap); Bitmap annotatedImage = outputPredictedImage.getAnnotatedImage(); double height = 1.5 * annotatedImage.getHeight(); double width = 1.5 * annotatedImage.getWidth(); scaledBitmap = Bitmap.createScaledBitmap(annotatedImage, (int) width, (int) height, true); imageView.setImageBitmap(scaledBitmap); - //TextView textView = getView().findViewById(R.id.textView2); - predictionOutput=outputPredictedImage.toString(); - textPredictionOutput.setText(outputPredictedImage.toString()); + predictionOutput = outputPredictedImage.toString(); + predictionResults = outputPredictedImage.getPredictionResults(); + tableResultsLayout.setVisibility(View.VISIBLE); + populateTable(); } catch (IOException e) { throw new RuntimeException(e); } catch (NoSpeciesRecognizedException e) { - Toast.makeText(getContext(), "L'application n'a pas reconnu d'espèce connue sur votre photo", Toast.LENGTH_SHORT).show(); - predictionOutput="nothing"; - textPredictionOutput.setText("Cette espèce n'est pas encore répertoriée."); + tableResultsLayout.setVisibility(View.INVISIBLE); + Toast.makeText(getContext(), "L'application n'a pas reconnu d'espèce connue sur votre photo.", Toast.LENGTH_LONG).show(); + predictionOutput = "nothing"; + } + } + + /** + * Adds a header row to the table layout. + */ + private void addHeaderRow() { + TableRow headerRow = new TableRow(getContext()); + headerRow.setBackgroundColor(getResources().getColor(R.color.myBlue)); // Optional: Add header styling + addTableCell(headerRow, "Espèce"); + addTableCell(headerRow, "Total individus"); + addTableCell(headerRow, "Précision(%)"); + tablePredictionResults.addView(headerRow); + } + + /** + * Populates the table with rows based on the prediction results. + */ + private void populateTable() { + if (predictionResults != null) { + // Clear the table to remove any previous rows + tablePredictionResults.removeAllViews(); + + // Add the header row + addHeaderRow(); + + // Add data rows for each prediction result + for (Map.Entry<String, RecognizeSpecie> entry : predictionResults.entrySet()) { + TableRow dataRow = new TableRow(getContext()); + + RecognizeSpecie specie = entry.getValue(); + + addTableCell(dataRow, entry.getKey()); // Species name + addTableCell(dataRow, String.valueOf(specie.getSpecieBoxes().size())); // Count + addTableCell(dataRow, String.format("%.2f", specie.getAverageProbability())); // Average probability + + tablePredictionResults.addView(dataRow); + } + } else { + // If no data exists, clear and add a message row + tablePredictionResults.removeAllViews(); // Clear existing rows + TableRow emptyRow = new TableRow(getContext()); + addTableCell(emptyRow, "No data available"); + tablePredictionResults.addView(emptyRow); } } + /** + * Adds a cell to a given table row. + * + * @param row The table row to which the cell will be added. + * @param text The text to display in the cell. + */ + private void addTableCell(TableRow row, String text) { + TextView textView = new TextView(getContext()); + textView.setText(text); + textView.setPadding(16, 16, 16, 16); + textView.setTextColor(getResources().getColor(R.color.white)); // Optional: Add text styling + row.addView(textView); + } + + /** + * Handles the results of a permission request, such as for camera access. + * + * @param requestCode Code indicating the request type. + * @param permissions Requested permissions. + * @param grantResults Results of the permission requests. + */ @RequiresApi(api = Build.VERSION_CODES.M) @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); + // Handle camera permission request results if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // Start the camera activity if permission is granted Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(cameraIntent, CAMERA_REQUEST_CODE); } else { + // Handle the case where permission is denied Log.e("PhotoPage", "Camera permission denied"); textPredictionOutput.setText("Camera permission denied. Please allow it to use this feature."); } } - } + /** + * Displays an overlay form for additional input. + */ public void showOverlayForm() { OverlayDialogFragmentForm overlayDialogForm = new OverlayDialogFragmentForm(); overlayDialogForm.show(getParentFragmentManager(), "overlayDialogForm"); } + /** + * Retrieves the prediction output string. + * + * @return The prediction output string. + */ public static String getPredictionOutput() { return predictionOutput; } diff --git a/app/src/main/java/com/example/myapplication/PublicActivity.java b/app/src/main/java/com/example/myapplication/PublicActivity.java index fe0336871e3cde8d7bd9a3befa5e7267f5acd643..ae8653ea3fe38b03820d5319dd1e19e57313812b 100644 --- a/app/src/main/java/com/example/myapplication/PublicActivity.java +++ b/app/src/main/java/com/example/myapplication/PublicActivity.java @@ -1,6 +1,5 @@ package com.example.myapplication; - import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; @@ -9,43 +8,60 @@ import android.os.Bundle; import com.google.android.material.bottomnavigation.BottomNavigationView; +/** + * PublicActivity represents an activity that hosts different fragments and provides navigation + * through a BottomNavigationView. It loads fragments based on the provided intent extra + * or the user's navigation choice. + */ public class PublicActivity extends AppCompatActivity { + + /** + * Called when the activity is created. This method initializes the UI, sets up the + * BottomNavigationView, and loads the correct fragment based on the "TARGET_FRAGMENT" + * provided in the intent or the user's navigation selection. + * + * @param savedInstanceState A bundle containing the saved state of the activity, + * or null if there was no previously saved state. + */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_public); + // Initialize the BottomNavigationView BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_navigation); - // Check for which fragment to load - String targetFragment=getIntent().getStringExtra("TARGET_FRAGMENT"); + // Get the target fragment name from the intent + String targetFragment = getIntent().getStringExtra("TARGET_FRAGMENT"); + + // Assert the fragment name is not null, then load the appropriate fragment assert targetFragment != null; switch (targetFragment) { case "LearningPage": - loadFragment(new LearningPage()); - bottomNavigationView.setSelectedItemId(R.id.navigation_apprendre); + loadFragment(new LearningPage()); // Load the LearningPage fragment + bottomNavigationView.setSelectedItemId(R.id.navigation_apprendre); // Select the appropriate bottom nav item break; case "StatsPage": - loadFragment(new StatsPage()); + loadFragment(new StatsPage()); // Load the StatsPage fragment bottomNavigationView.setSelectedItemId(R.id.navigation_statistiques); break; case "SettingsPage": + // Launch a new instance of PublicActivity to load the SettingsPage fragment Intent intent = new Intent(PublicActivity.this, PublicActivity.class); intent.putExtra("TARGET_FRAGMENT", "SettingsPage"); loadFragment(new SettingsPage()); bottomNavigationView.setSelectedItemId(R.id.navigation_parametres); break; case "PhotoPage": - loadFragment(new PhotoPage()); + loadFragment(new PhotoPage()); // Load the PhotoPage fragment bottomNavigationView.setSelectedItemId(R.id.navigation_identifier); break; } - // Set up navigation item selection - bottomNavigationView.setOnNavigationItemSelectedListener(item -> - - { + // Set up the BottomNavigationView item selection listener + bottomNavigationView.setOnNavigationItemSelectedListener(item -> { Fragment selectedFragment; + // Check the selected navigation item and load the corresponding fragment if (item.getItemId() == R.id.navigation_apprendre) { selectedFragment = new LearningPage(); } else if (item.getItemId() == R.id.navigation_statistiques) { @@ -55,21 +71,27 @@ public class PublicActivity extends AppCompatActivity { } else if (item.getItemId() == R.id.navigation_identifier) { selectedFragment = new PhotoPage(); } else { - selectedFragment=null; + // If none of the known items are selected, navigate back to HomeActivity + selectedFragment = null; Intent intent = new Intent(PublicActivity.this, HomeActivity.class); startActivity(intent); finish(); } - loadFragment(selectedFragment); + loadFragment(selectedFragment); // Load the selected fragment return true; }); } + /** + * Loads the specified fragment into the fragment container. + * + * @param fragment The fragment to load. + */ private void loadFragment(Fragment fragment) { + // Begin a fragment transaction to replace the current fragment with the new one getSupportFragmentManager() .beginTransaction() - .replace(R.id.fragment_container, fragment) - .commit(); + .replace(R.id.fragment_container, fragment) // Replace the current fragment + .commit(); // Commit the transaction } } - diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentBecasseau.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentBecasseau.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentBecasseau.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentBecasseau.java index 43092ee17f22f621cc9827387e84ab273ad2dde7..a009906202061df0ebe7065838efe13a30257419 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentBecasseau.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentBecasseau.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentBecasseau extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentBernache.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentBernache.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentBernache.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentBernache.java index e93978725d2fb0fbedce7a9aaa6b09561058d848..a1139487b22cf77cc751eafbf9ac376a6705d5f4 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentBernache.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentBernache.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentBernache extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentChevalier.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentChevalier.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentChevalier.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentChevalier.java index 09673ec52c06505f2061aa608f93d167918fa517..c412b7720ccff994957926c9d31a8c5da9b4f6cc 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentChevalier.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentChevalier.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentChevalier extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentCormoran.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentCormoran.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentCormoran.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentCormoran.java index 16bc12f0608f132706f908f485efd2d8feb37f6d..c5724c2c94ad8f55778df7da9168ec36805a203e 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentCormoran.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentCormoran.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentCormoran extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentForm.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentForm.java similarity index 98% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentForm.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentForm.java index 1194bf3dde8e58fd41c1236870f8cc624653de4c..c9141a6a193256582711bde3b7bb9ab32d582c9f 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentForm.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentForm.java @@ -1,7 +1,5 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; -import android.app.AlertDialog; -import android.content.DialogInterface; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; @@ -14,13 +12,16 @@ import android.widget.Button; import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; -import android.widget.TimePicker; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.GoogleFormApi; +import com.example.myapplication.PhotoPage; +import com.example.myapplication.R; + import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentFoulque.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentFoulque.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentFoulque.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentFoulque.java index 324ad8539cf333ba7a1d3cb278046bdb5cf3081c..1a12ad25cdb7ece0ef83b62f87756a8a0602b113 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentFoulque.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentFoulque.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentFoulque extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentGoeland.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentGoeland.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentGoeland.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentGoeland.java index ffc31366f988684c6acf9af72ed84f4c9a579e76..a0c7d91da2100b65f3cc7271a21cac0e0c40f6ca 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentGoeland.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentGoeland.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentGoeland extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentMouette.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentMouette.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentMouette.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentMouette.java index 47948e28e67e1d29d1efd65d0e156db30e49a8ae..862a71e693f6932ba1bc17c409df63788b8a4197 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentMouette.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentMouette.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentMouette extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentPluvier.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentPluvier.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentPluvier.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentPluvier.java index 7fab249a17260288785e28ea4d0c6acee16cc0bc..fa6a4ac6f8f1a73723ff1cef3ed09af1da3f8347 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentPluvier.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentPluvier.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentPluvier extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentTadorne.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentTadorne.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentTadorne.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentTadorne.java index 1ca876cb47d5b75d0271a8f8c3454c3f5b409562..371958f6161f4f2e237e20adf46b8053bdea8f22 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentTadorne.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentTadorne.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentTadorne extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentZonage.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentZonage.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentZonage.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentZonage.java index 0c3d5b0d8ae3ed1d50cb2e98d84ac4417659f832..b17dcaab9a229bc356ae015ca24ce6e8a4d651b6 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentZonage.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentZonage.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentZonage extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/BoundingBox.java b/app/src/main/java/com/example/myapplication/recognation_logic/BoundingBox.java similarity index 92% rename from app/src/main/java/com/example/myapplication/BoundingBox.java rename to app/src/main/java/com/example/myapplication/recognation_logic/BoundingBox.java index 20110f1437f315d11d601bf1bcaa581080e56432..6396e59ef6fd39a1e1874654fce00e243c01444d 100644 --- a/app/src/main/java/com/example/myapplication/BoundingBox.java +++ b/app/src/main/java/com/example/myapplication/recognation_logic/BoundingBox.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.recognation_logic; public class BoundingBox { public float x1, y1, x2, y2, cx, cy, w, h, cnf; diff --git a/app/src/main/java/com/example/myapplication/NoSpeciesRecognizedException.java b/app/src/main/java/com/example/myapplication/recognation_logic/NoSpeciesRecognizedException.java similarity index 55% rename from app/src/main/java/com/example/myapplication/NoSpeciesRecognizedException.java rename to app/src/main/java/com/example/myapplication/recognation_logic/NoSpeciesRecognizedException.java index a23697cd73f2b89794485f176c7dc6b9609d55b2..fd5f1dfd8333203d0b06f09645e31900a51564e5 100644 --- a/app/src/main/java/com/example/myapplication/NoSpeciesRecognizedException.java +++ b/app/src/main/java/com/example/myapplication/recognation_logic/NoSpeciesRecognizedException.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.recognation_logic; public class NoSpeciesRecognizedException extends Exception{ diff --git a/app/src/main/java/com/example/myapplication/OutputPredictedImage.java b/app/src/main/java/com/example/myapplication/recognation_logic/OutputPredictedImage.java similarity index 65% rename from app/src/main/java/com/example/myapplication/OutputPredictedImage.java rename to app/src/main/java/com/example/myapplication/recognation_logic/OutputPredictedImage.java index 950de8ae765b65bc013c9dc9a00fc32ad0106f90..2f4e005759f9a6095aaf39f2973819af7aea4200 100644 --- a/app/src/main/java/com/example/myapplication/OutputPredictedImage.java +++ b/app/src/main/java/com/example/myapplication/recognation_logic/OutputPredictedImage.java @@ -1,7 +1,6 @@ -package com.example.myapplication; +package com.example.myapplication.recognation_logic; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import java.util.Map; @@ -10,21 +9,20 @@ import java.util.Map; */ public class OutputPredictedImage { private Bitmap annotatedImage; - private Map<String, Integer> predictionResults; + private Map<String, RecognizeSpecie> predictionResults; - public OutputPredictedImage(Bitmap annotatedImage, Map<String,Integer> predictionResults){ + public OutputPredictedImage(Bitmap annotatedImage, Map<String,RecognizeSpecie> predictionResults){ this.annotatedImage = annotatedImage; this.predictionResults = predictionResults; } + @Override public String toString(){ StringBuilder result = new StringBuilder(); - for (Map.Entry<String, Integer> entry : predictionResults.entrySet()) { + for (Map.Entry<String, RecognizeSpecie> entry : predictionResults.entrySet()) { // Append the key (species) and value (probability) in the specified format - result.append(entry.getKey()) - .append(" : ") - .append(entry.getValue()) // Format the float to 2 decimal places + result.append(entry.getValue().toString()) // Format the float to 2 decimal places .append("\n"); // Add a newline for separation } @@ -36,7 +34,7 @@ public class OutputPredictedImage { return annotatedImage; } - public Map<String, Integer> getPredictionResults() { + public Map<String, RecognizeSpecie> getPredictionResults() { return predictionResults; } diff --git a/app/src/main/java/com/example/myapplication/recognation_logic/RecognizeSpecie.java b/app/src/main/java/com/example/myapplication/recognation_logic/RecognizeSpecie.java new file mode 100644 index 0000000000000000000000000000000000000000..de3f52a9c95700677f246ce6a597d66cb67057f7 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/recognation_logic/RecognizeSpecie.java @@ -0,0 +1,41 @@ +package com.example.myapplication.recognation_logic; + +import java.io.Serializable; +import java.util.List; + +public class RecognizeSpecie implements Serializable { + private String nameRecognizedSpecie; + private List<BoundingBox> specieBoxes; + + RecognizeSpecie(String nameRecognizedSpecie, List<BoundingBox> specieBoxes){ + this.nameRecognizedSpecie = nameRecognizedSpecie; + this.specieBoxes = specieBoxes; + } + + public String getNameRecognizedSpecie() { + return nameRecognizedSpecie; + } + + public List<BoundingBox> getSpecieBoxes() { + return specieBoxes; + } + + public String toString(){ + int totalNumber = specieBoxes.size(); + float sumProbability = 0; + for(BoundingBox boundingBox:specieBoxes){ + sumProbability += boundingBox.cnf; + } + String meanProbability = String.format("%.2f", 100 * (sumProbability / totalNumber)); + return nameRecognizedSpecie + " : " + totalNumber + "; precision " + meanProbability +"%"; + } + + public float getAverageProbability() { + int totalNumber = specieBoxes.size(); + float sumProbability = 0; + for(BoundingBox boundingBox:specieBoxes){ + sumProbability += boundingBox.cnf; + } + return 100 * (sumProbability / totalNumber); + } +} diff --git a/app/src/main/java/com/example/myapplication/TestModeleTflite.java b/app/src/main/java/com/example/myapplication/recognation_logic/TestModeleTflite.java similarity index 65% rename from app/src/main/java/com/example/myapplication/TestModeleTflite.java rename to app/src/main/java/com/example/myapplication/recognation_logic/TestModeleTflite.java index 0a1c267ea8d76698decbf36d0631270914e86d0b..3f0a504bd47ceaf96928d35cc77785aa89146bbf 100644 --- a/app/src/main/java/com/example/myapplication/TestModeleTflite.java +++ b/app/src/main/java/com/example/myapplication/recognation_logic/TestModeleTflite.java @@ -1,10 +1,9 @@ -package com.example.myapplication; +package com.example.myapplication.recognation_logic; import static android.graphics.Paint.*; import android.content.Context; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; @@ -20,8 +19,6 @@ import org.tensorflow.lite.support.image.TensorImage; import org.tensorflow.lite.support.tensorbuffer.TensorBuffer; import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -31,6 +28,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +/** + * A class for performing species recognition using a TensorFlow Lite model. + * Handles model loading, inference, output processing, and annotation of input images. + */ public class TestModeleTflite { private static final String MODEL_PATH = "model_float32.tflite"; private static final String LABEL_PATH = "labels.txt"; @@ -39,7 +40,7 @@ public class TestModeleTflite { private static final float INPUT_STANDARD_DEVIATION = 255f; private static final DataType INPUT_IMAGE_TYPE = DataType.FLOAT32; private static final DataType OUTPUT_IMAGE_TYPE = DataType.FLOAT32; - private static final float CONFIDENCE_THRESHOLD = 0.3F; + private static final float CONFIDENCE_THRESHOLD = 0.4F; private static final float IOU_THRESHOLD = 0.5F; private Interpreter interpreter; @@ -50,16 +51,22 @@ public class TestModeleTflite { private List<String> labels = new ArrayList<>(); private ImageProcessor imageProcessor; + /** + * Initializes the TensorFlow Lite model interpreter and loads associated resources. + * + * @param context The application context for accessing assets. + * @throws IOException If the model or label files cannot be loaded. + */ public TestModeleTflite(Context context) throws IOException { - // Load the model + // Load the TensorFlow Lite model MappedByteBuffer model = FileUtil.loadMappedFile(context, MODEL_PATH); - // Set interpreter options + // Configure the interpreter with multithreading options Interpreter.Options options = new Interpreter.Options(); options.setNumThreads(4); interpreter = new Interpreter(model, options); - // Get tensor shapes + // Extract tensor shapes from the model int[] inputShape = interpreter.getInputTensor(0).shape(); int[] outputShape = interpreter.getOutputTensor(0).shape(); @@ -68,7 +75,7 @@ public class TestModeleTflite { numChannel = outputShape[1]; numElements = outputShape[2]; - // Load labels + // Load the label list try (InputStream inputStream = context.getAssets().open(LABEL_PATH); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { String line; @@ -77,54 +84,99 @@ public class TestModeleTflite { } } - // Initialize image processor + // Configure preprocessing steps for the input image imageProcessor = new ImageProcessor.Builder() .add(new NormalizeOp(INPUT_MEAN, INPUT_STANDARD_DEVIATION)) .add(new CastOp(INPUT_IMAGE_TYPE)) .build(); } + /** + * Prepares an input image and performs inference using the TensorFlow Lite model. + * + * @param bitmap The input image to analyze. + * @return A TensorBuffer containing the model's raw output. + */ public TensorBuffer predict(Bitmap bitmap) { - // Resize bitmap to model's input size Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, tensorWidth, tensorHeight, false); - // Preprocess input image TensorImage tensorImage = new TensorImage(DataType.FLOAT32); tensorImage.load(resizedBitmap); TensorImage processedImage = imageProcessor.process(tensorImage); - // Prepare output buffer TensorBuffer outputBuffer = TensorBuffer.createFixedSize( new int[]{1, numChannel, numElements}, OUTPUT_IMAGE_TYPE ); - // Run inference interpreter.run(processedImage.getBuffer(), outputBuffer.getBuffer()); - return outputBuffer; } - public List<String> getLabels() { - return labels; - } - - public void close() { - if (interpreter != null) { - interpreter.close(); - interpreter = null; - } - } - + /** + * Processes the raw model output to generate bounding boxes for detected objects. + * + * @param outputBuffer The TensorBuffer containing raw model output. + * @return A list of bounding boxes for detected objects. + */ public List<BoundingBox> processOutput(TensorBuffer outputBuffer) { float[] outputArray = outputBuffer.getFloatArray(); return bestBox(outputArray); } + /** + * Annotates an input image with bounding boxes for detected objects. + * + * @param inputImage The original image to annotate. + * @param boxes The bounding boxes of detected objects. + * @return A new image with bounding boxes drawn. + */ public Bitmap annotateImage(Bitmap inputImage, List<BoundingBox> boxes) { return drawBoundingBoxes(inputImage, boxes); } + /** + * Groups detections by class and constructs a structured output image and metadata. + * + * @param inputImage The input image for recognition. + * @return An OutputPredictedImage containing the annotated image and prediction results. + * @throws NoSpeciesRecognizedException If no objects are detected. + */ + public OutputPredictedImage recognizeSpeciesClass(Bitmap inputImage) throws NoSpeciesRecognizedException { + Map<String, RecognizeSpecie> predictionResults = new HashMap<>(); + + TensorBuffer outputBuffer = predict(inputImage); + List<BoundingBox> boxes = processOutput(outputBuffer); + if (boxes != null && !boxes.isEmpty()) { + for (BoundingBox box : boxes) { + if (predictionResults.containsKey(box.clsName)) { + predictionResults.get(box.clsName).getSpecieBoxes().add(box); + } else { + predictionResults.put(box.clsName, new RecognizeSpecie(box.clsName, new ArrayList<>())); + predictionResults.get(box.clsName).getSpecieBoxes().add(box); + } + } + Bitmap annotatedImage = annotateImage(inputImage, boxes); + return new OutputPredictedImage(annotatedImage, predictionResults); + } else { + throw new NoSpeciesRecognizedException(); + } + + } + + /** + * Releases the resources held by the TensorFlow Lite interpreter. + */ + public void close() { + if (interpreter != null) { + interpreter.close(); + interpreter = null; + } + } + + /** + * Filters bounding boxes based on confidence and non-maximum suppression (NMS). + */ private List<BoundingBox> bestBox(float[] array) { List<BoundingBox> boundingBoxes = new ArrayList<>(); @@ -161,26 +213,29 @@ public class TestModeleTflite { } } - if (boundingBoxes.isEmpty()) return null; - return applyNMS(boundingBoxes); } + /** + * Applies non-maximum suppression (NMS) to eliminate overlapping boxes. + */ private List<BoundingBox> applyNMS(List<BoundingBox> boxes) { List<BoundingBox> sortedBoxes = new ArrayList<>(boxes); - sortedBoxes.sort((b1, b2) -> Float.compare(b2.cnf, b1.cnf)); // Sort by confidence descending + sortedBoxes.sort((b1, b2) -> Float.compare(b2.cnf, b1.cnf)); List<BoundingBox> selectedBoxes = new ArrayList<>(); while (!sortedBoxes.isEmpty()) { BoundingBox first = sortedBoxes.remove(0); selectedBoxes.add(first); - sortedBoxes.removeIf(nextBox -> calculateIoU(first, nextBox) >= IOU_THRESHOLD); } return selectedBoxes; } + /** + * Calculates the Intersection over Union (IoU) between two bounding boxes. + */ private float calculateIoU(BoundingBox box1, BoundingBox box2) { float x1 = Math.max(box1.x1, box2.x1); float y1 = Math.max(box1.y1, box2.y1); @@ -194,21 +249,24 @@ public class TestModeleTflite { return intersectionArea / (box1Area + box2Area - intersectionArea); } - - + /** + * Draws bounding boxes on an image. + */ private Bitmap drawBoundingBoxes(Bitmap bitmap, List<BoundingBox> boxes) { Bitmap mutableBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true); Canvas canvas = new Canvas(mutableBitmap); + // Scale stroke width dynamically based on image resolution + float strokeWidth = Math.max(mutableBitmap.getWidth(), mutableBitmap.getHeight()) / 470.0f; + Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Style.STROKE); - paint.setStrokeWidth(8f); + paint.setStrokeWidth(strokeWidth); // Dynamically set stroke width Paint textPaint = new Paint(); textPaint.setColor(Color.WHITE); - textPaint.setTextSize(40f); - //textPaint.setTypeface(Paint.DEFAULT_BOLD); + textPaint.setStyle(Style.FILL); for (BoundingBox box : boxes) { RectF rect = new RectF( @@ -217,88 +275,36 @@ public class TestModeleTflite { box.x2 * mutableBitmap.getWidth(), box.y2 * mutableBitmap.getHeight() ); - canvas.drawRect(rect, paint); - canvas.drawText(box.clsName, rect.left, rect.bottom, textPaint); - canvas.drawText(new StringBuilder().append("prob : ").append(String.format("%.2f", box.cnf)).toString(), rect.left, rect.top, textPaint); - } - - return mutableBitmap; - } - - /** - * Loads an image from the specified file path and converts it to a Bitmap. - * - * @param fileName Path to the image file. - * @return Bitmap representation of the image or null if an error occurs. - */ - public static Bitmap loadImage(String fileName, Context context) { - try (InputStream inputStream = context.getAssets().open(fileName)) { - return BitmapFactory.decodeStream(inputStream); - } catch (IOException e) { - e.printStackTrace(); - return null; - } - } - public Bitmap recognizeSpecies(Bitmap inputImage){ - TensorBuffer outputBuffer = predict(inputImage); - List<BoundingBox> boxes = processOutput(outputBuffer); - return annotateImage(inputImage, boxes); - } + // Draw the bounding box + canvas.drawRect(rect, paint); - public OutputPredictedImage recognizeSpeciesClass(Bitmap inputImage) throws NoSpeciesRecognizedException { - //ArrayList<String> speciesNames = new ArrayList<>(); - Map<String, Integer> predictionResults = new HashMap<>(); - TensorBuffer outputBuffer = predict(inputImage); - List<BoundingBox> boxes = processOutput(outputBuffer); - if(boxes != null){ - for(BoundingBox box:boxes){ - if(predictionResults.containsKey(box.clsName)){ - int previousNumber = predictionResults.get(box.clsName); - predictionResults.put(box.clsName, previousNumber + 1); - }else{ - predictionResults.put(box.clsName, 1); - } + // Dynamically adjust text size to fit within the box + float boxWidth = rect.width(); + float boxHeight = rect.height(); + float maxTextSize = boxHeight * 0.2f; // Limit text size to a fraction of the box height + textPaint.setTextSize(maxTextSize); + // Measure text width and adjust if necessary + float textWidth = textPaint.measureText(box.clsName); + if (textWidth > boxWidth) { + textPaint.setTextSize(maxTextSize * (boxWidth / textWidth)); } - Bitmap annotatedImage = annotateImage(inputImage, boxes); - return new OutputPredictedImage(annotatedImage, predictionResults); - }else throw new NoSpeciesRecognizedException(); - } - - public static void main(String [] args){ - /* - TestModeleTflite testModeleTflite = new TestModeleTflite(this); - String imagePath = "../../res/drawable/bernache_cravant.jpg"; - Bitmap inputImage = TestModeleTflite.loadImage(imagePath); - - if (inputImage == null) { - System.out.println("Failed to load the image. Check the file path."); - return; + // Draw the text at the bottom of the box + canvas.drawText(box.clsName, rect.left, rect.bottom - 5, textPaint); // Adjust -5 for padding } - // Perform prediction - TensorBuffer outputBuffer = testModeleTflite.predict(inputImage); + return mutableBitmap; + } - // Process the output to get bounding boxes - List<BoundingBox> boxes = testModeleTflite.processOutput(outputBuffer); - if (boxes == null || boxes.isEmpty()) { - System.out.println("No objects detected in the image."); - } else { - // Output results to the console - for (BoundingBox box : boxes) { - System.out.println("Class: " + box.clsName); - System.out.println("Confidence: " + box.cnf); - System.out.println("----------------------------"); - } - }*/ + + /** + * Main method for debugging or testing purposes. + */ + public static void main(String[] args) { String currentDir = System.getProperty("user.dir"); System.out.println("Current working directory: " + currentDir); } - - - } - diff --git a/app/src/main/java/com/example/myapplication/TutorialActivity.java b/app/src/main/java/com/example/myapplication/tutorials/TutorialActivity.java similarity index 95% rename from app/src/main/java/com/example/myapplication/TutorialActivity.java rename to app/src/main/java/com/example/myapplication/tutorials/TutorialActivity.java index 9749d0026ec28f764835c03ab945d1071d485d4f..5605f67d41c8273449049df7e6301f8bc1f455cd 100644 --- a/app/src/main/java/com/example/myapplication/TutorialActivity.java +++ b/app/src/main/java/com/example/myapplication/tutorials/TutorialActivity.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.tutorials; import android.content.Intent; import android.os.Bundle; @@ -11,6 +11,9 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import androidx.viewpager2.widget.ViewPager2; +import com.example.myapplication.HomeActivity; +import com.example.myapplication.R; + import java.util.Arrays; import java.util.List; diff --git a/app/src/main/java/com/example/myapplication/TutorialFragmentAdapter.java b/app/src/main/java/com/example/myapplication/tutorials/TutorialFragmentAdapter.java similarity index 94% rename from app/src/main/java/com/example/myapplication/TutorialFragmentAdapter.java rename to app/src/main/java/com/example/myapplication/tutorials/TutorialFragmentAdapter.java index 1deec3b60cf50807ef9c57a7af0f5620bce3e79b..de09261ac8a7f27181e649432f9795a97fbfa8f7 100644 --- a/app/src/main/java/com/example/myapplication/TutorialFragmentAdapter.java +++ b/app/src/main/java/com/example/myapplication/tutorials/TutorialFragmentAdapter.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.tutorials; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; diff --git a/app/src/main/java/com/example/myapplication/TutorialPageFragment.java b/app/src/main/java/com/example/myapplication/tutorials/TutorialPageFragment.java similarity index 93% rename from app/src/main/java/com/example/myapplication/TutorialPageFragment.java rename to app/src/main/java/com/example/myapplication/tutorials/TutorialPageFragment.java index 803cea3530f0dd9caa401dd308558164ef06744d..89ff29c5885ef7168d1fe688c57ff8d3e5148fdc 100644 --- a/app/src/main/java/com/example/myapplication/TutorialPageFragment.java +++ b/app/src/main/java/com/example/myapplication/tutorials/TutorialPageFragment.java @@ -1,10 +1,9 @@ -package com.example.myapplication; +package com.example.myapplication.tutorials; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; import androidx.annotation.NonNull; import androidx.annotation.Nullable; diff --git a/app/src/main/res/layout/activity_tutorial.xml b/app/src/main/res/layout/activity_tutorial.xml index 4f6b328cb67b42118e3df396511285f24f9e1bea..ecb37940b454bc0c8158f93bd00157c90a66b2e7 100644 --- a/app/src/main/res/layout/activity_tutorial.xml +++ b/app/src/main/res/layout/activity_tutorial.xml @@ -6,7 +6,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/black" - tools:context=".TutorialActivity"> + tools:context=".tutorials.TutorialActivity"> <ImageView android:id="@+id/image_1" diff --git a/app/src/main/res/layout/fragment_photo_page.xml b/app/src/main/res/layout/fragment_photo_page.xml index a6148b99adaf2cb3b1f73b9347bc99d4537c5479..903b689287fa33b6c2dd9c52a693b54be9262236 100644 --- a/app/src/main/res/layout/fragment_photo_page.xml +++ b/app/src/main/res/layout/fragment_photo_page.xml @@ -73,7 +73,7 @@ android:layout_marginTop="20dp" android:fontFamily="@font/inter" android:paddingHorizontal="40dp" - android:text="Voir espèce(s) identifiée(s)" + android:text="Espèce(s) identifiée(s)" android:textSize="15sp" /> </FrameLayout> @@ -85,43 +85,46 @@ <TextView - android:id="@+id/textPredictionOutput" - android:layout_width="351dp" - android:layout_height="28dp" - android:layout_gravity="bottom|center_horizontal" - android:layout_marginBottom="146dp" - android:text="" - android:textColor="#F12B2B" - android:textSize="17sp" - android:textStyle="bold" /> - - - <TextView - android:id="@+id/prediction_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal|bottom" - android:layout_marginBottom="186dp" - android:text="PREDICTION:" - android:textColor="#D9D9D9" - android:textSize="24sp" - android:textStyle="bold" /> - - <TextView - style="@style/PageTitleAppearance" android:id="@+id/learning_page_title" + style="@style/PageTitleAppearance" android:layout_marginTop="50dp" android:layout_marginBottom="60dp" android:paddingTop="30dp" android:paddingEnd="50dp" android:text="@string/photo" /> - <EditText - android:id="@+id/editTextTextMultiLine4" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:ems="10" - android:gravity="start|top" - android:inputType="textMultiLine" /> + + <FrameLayout + style="@style/TopTopAppearance" + android:id="@+id/tableResultsLayout" + android:layout_width="362dp" + android:layout_height="90dp" + android:layout_gravity="center_horizontal|bottom" + android:layout_marginBottom="110dp"> + + <ScrollView + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="7dp"> + + <TableLayout + android:id="@+id/results_table1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:stretchColumns="1"> + <!-- Rows will be added dynamically --> + </TableLayout> + + </LinearLayout> + </ScrollView> + + </FrameLayout> + + </FrameLayout> diff --git a/app/src/main/res/layout/fragment_prediction_results.xml b/app/src/main/res/layout/fragment_prediction_results.xml new file mode 100644 index 0000000000000000000000000000000000000000..ad2ed74fde99dbb0b992d321d5b77d88fab20b3f --- /dev/null +++ b/app/src/main/res/layout/fragment_prediction_results.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="utf-8"?> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".PhotoPage"> + + + <FrameLayout + style="@style/TopTopAppearance" + android:layout_width="362dp" + android:layout_height="120dp" + android:layout_gravity="center"> + + <ScrollView + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="16dp"> + + <TableLayout + android:id="@+id/results_table" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:stretchColumns="1"> + <!-- Rows will be added dynamically --> + </TableLayout> + + </LinearLayout> + </ScrollView> + + + </FrameLayout> + + <TextView + android:id="@+id/prediction_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal|top" + android:layout_marginTop="320dp" + android:text="Détails de la Prédiction:" + android:textColor="#D9D9D9" + android:textSize="30sp" + android:textStyle="bold" /> + + <Button + android:id="@+id/backToPrediction" + style="@style/ButtonAppearance" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom|end" + android:layout_marginBottom="300dp" + android:fontFamily="@font/inter" + android:text="Retour" + android:textSize="20sp" /> + + + + + + + <TextView + style="@style/PageTitleAppearance" + android:id="@+id/learning_page_title" + android:layout_marginTop="50dp" + android:layout_marginBottom="60dp" + android:paddingTop="30dp" + android:paddingEnd="50dp" + android:text="@string/photo" /> + + +</FrameLayout> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index c8524cd961d27b6695e755c6ef2d4d58cf38431e..edd55aba2128f737aefd9a04083f85d52e3cc49a 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -2,4 +2,5 @@ <resources> <color name="black">#FF000000</color> <color name="white">#FFFFFFFF</color> + <color name="myBlue">#517293</color> </resources> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1272c73827e38d8321de1f6783c7fd67d9833af6..8f813d0a20d69b4b665b6c90371e2661f308af20 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -82,7 +82,7 @@ <string name="mode_sombre_texte">Le mode sombre économise votre batterie et lutte contre la fatigue visuelle</string> <string name="download_text">Télécharge un guide d’utilisation approfondi de l’application au format pdf</string> <string name="photo_time_text">Confirmez l\'heure de la prise de la photo</string> - <string name="locate_text">Localisation (qu\'est ce que vous voyez?)</string> + <string name="locate_text">Localisation (qu\'est ce que vous voyez autour de vous? ex: un arbre, un rocher, autre?)</string> <string name="send">Envoyer</string> <string name="desc_text">Que voyez-vous autour de vous ?</string> <string name="zone_info_text">Plus d\'info sur les zones</string>