diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3244b422655b480beaefc14fd2899b49fdee642d..a146d57153e7b8e7f02352fc390c1977118b628c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -45,4 +45,6 @@ dependencies { testImplementation(libs.junit) androidTestImplementation(libs.ext.junit) androidTestImplementation(libs.espresso.core) + implementation(libs.retrofit2.retrofit) + implementation(libs.retrofit2.converter.gson) } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5b948718e24417d8dc35a3af094f92fe24e715da..44c862e9a4a77e4a0a35926bf7bfcc68c8e02d5a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,6 +12,8 @@ android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.CAMERA" /> + <uses-permission android:name="android.permission.INTERNET"/> + <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" diff --git a/app/src/main/java/com/example/myapplication/GoogleFormApi.java b/app/src/main/java/com/example/myapplication/GoogleFormApi.java new file mode 100644 index 0000000000000000000000000000000000000000..93f7f4f024087e43861e782c0a75c469c18c206d --- /dev/null +++ b/app/src/main/java/com/example/myapplication/GoogleFormApi.java @@ -0,0 +1,32 @@ +package com.example.myapplication; + +import retrofit2.Call; +import retrofit2.http.Field; +import retrofit2.http.FormUrlEncoded; +import retrofit2.http.POST; + +public interface GoogleFormApi { + + // Sends form data to the specified Google Form + @FormUrlEncoded + @POST("/forms/d/e/1FAIpQLScC1-0J39OuIBUedhf_xK_w7qYT8GXVtVpl7SvL4yCPN0VLWA/formResponse") + Call<Void> sendFormData( + // Birds fields + @Field("entry.592245432") String becasseauNumber, + @Field("entry.1372224824") String bernacheNumber, + @Field("entry.582994154") String goelandNumber, + @Field("entry.608828728") String mouetteNumber, + @Field("entry.2059411273") String pluvierNumber, + @Field("entry.2099434669") String cormoranNumber, + @Field("entry.1505904869") String foulqueNumber, + @Field("entry.1113424666") String tadorneNumber, + @Field("entry.1340159476") String chevalierNumber, + + // Other fields + @Field("entry.311834827") String unknownSpecies, + @Field("entry.1710251376") String zone, + @Field("entry.1284627072") String surroundings, + @Field("entry.1321597415") String time + ); +} + diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentForm.java b/app/src/main/java/com/example/myapplication/OverlayDialogFragmentForm.java index 66be8c49f7c091e4d26f722a5675cc59752d5db1..4efcdceff0b81a9e3bde6b5d2845e071186e5915 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentForm.java +++ b/app/src/main/java/com/example/myapplication/OverlayDialogFragmentForm.java @@ -1,29 +1,64 @@ package com.example.myapplication; -import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.AdapterView; import android.widget.ArrayAdapter; 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 retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + public class OverlayDialogFragmentForm extends DialogFragment { + private String zoneSelected; + private boolean isZoneSelected = false; + + private String time; + private boolean isTimeSelected = false; + + private String finalPredictionOutput; + + private String becasseauCounter = "0"; + private String bernacheCounter = "0"; + private String goelandCounter = "0"; + private String mouetteCounter = "0"; + private String pluvierCounter = "0"; + private String cormoranCounter = "0"; + private String foulqueCounter = "0"; + private String tadorneCounter = "0"; + private String chevalierCounter = "0"; + + private String surroundingsDescription; + + // Linking the Google Forms + private final Retrofit retrofit = new Retrofit.Builder().baseUrl("https://docs.google.com/").addConverterFactory(GsonConverterFactory.create()).build(); + + private final GoogleFormApi api = retrofit.create(GoogleFormApi.class); + @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { // Inflate the layout for this dialog fragment View view = inflater.inflate(R.layout.fragment_form, container, false); - return inflater.inflate(R.layout.fragment_form, container, false); + return view; } @Override @@ -33,20 +68,6 @@ public class OverlayDialogFragmentForm extends DialogFragment { getDialog().getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); getDialog().getWindow().setBackgroundDrawableResource(android.R.color.transparent); // Transparent background } - Button goBackButton=(Button) this.getView().findViewById(R.id.submit_form_button); - - View.OnClickListener listenerGoBackButton=new View.OnClickListener() { - @Override - public void onClick(View view) { - goBack(); - } - }; - - goBackButton.setOnClickListener(listenerGoBackButton); - } - - private void goBack() { - this.getDialog().dismiss(); } @Override @@ -65,13 +86,188 @@ public class OverlayDialogFragmentForm extends DialogFragment { // Set the list of options in the dropdown menu zoneSelector.setAdapter(adapter); + // If an item is selected we retain its value + zoneSelector.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + zoneSelected = parent.getItemAtPosition(position).toString(); + isZoneSelected = true; + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + isZoneSelected = false; + } + }); + + // Set the time picker settings TimePicker timePicker = view.findViewById(R.id.time_picker); timePicker.setIs24HourView(true); + time = timePicker.getHour() + ":" + timePicker.getMinute(); + // If time is changed we retain its value + timePicker.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener() { + @Override + public void onTimeChanged(TimePicker timePicker, int i, int i1) { + time = timePicker.getHour() + ":" + timePicker.getMinute(); + isTimeSelected = true; + } + }); // Display the result in the form + finalPredictionOutput = PhotoPage.getPredictionOutput(); TextView predictionDisplay = view.findViewById(R.id.prediction_output); - String predictionText = "Nous avons détecté sur votre photo :\n" + PhotoPage.getPredictionOutput(); + String predictionText = "Nous avons détecté sur votre photo :\n" + finalPredictionOutput; predictionDisplay.setText(predictionText); + + String[] numberBySpecies = finalPredictionOutput.split("\n"); + + // See how many birds of each species were identified + if (finalPredictionOutput.contains("Bécasseau Sanderling")) { + for (String species : numberBySpecies) { + if (species.contains("Bécasseau Sanderling")) { + becasseauCounter = species.split(" : ")[1]; + } + } + } + + if (finalPredictionOutput.contains("Bernache Cravant")) { + for (String species : numberBySpecies) { + if (species.contains("Bernache Cravant")) { + bernacheCounter = species.split(" : ")[1]; + } + } + } + + if (finalPredictionOutput.contains("Goéland Argenté")) { + for (String species : numberBySpecies) { + if (species.contains("Goéland Argenté")) { + goelandCounter = species.split(" : ")[1]; + } + } + } + + if (finalPredictionOutput.contains("Mouette Rieuse")) { + for (String species : numberBySpecies) { + if (species.contains("Mouette Rieuse")) { + mouetteCounter = species.split(" : ")[1]; + } + } + } + + if (finalPredictionOutput.contains("Pluvier Argenté")) { + for (String species : numberBySpecies) { + if (species.contains("Pluvier Argenté")) { + pluvierCounter = species.split(" : ")[1]; + } + } + } + + if (finalPredictionOutput.contains("Grand Cormoran")) { + for (String species : numberBySpecies) { + if (species.contains("Grand Cormoran")) { + cormoranCounter = species.split(" : ")[1]; + } + } + } + + if (finalPredictionOutput.contains("Foulque Macroule")) { + for (String species : numberBySpecies) { + if (species.contains("Foulque Macroule")) { + foulqueCounter = species.split(" : ")[1]; + } + } + } + + if (finalPredictionOutput.contains("Tadorne de Belon")) { + for (String species : numberBySpecies) { + if (species.contains("Tadorne de Belon")) { + tadorneCounter = species.split(" : ")[1]; + } + } + } + + if (finalPredictionOutput.contains("Chevalier Gambette")) { + for (String species : numberBySpecies) { + if (species.contains("Chevalier Gambette")) { + chevalierCounter = species.split(" : ")[1]; + } + } + } + + EditText surroundings = view.findViewById(R.id.location_input); + surroundings.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { + surroundingsDescription = "Pas de description fournie"; + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { + surroundingsDescription = surroundings.getText().toString(); + } + + @Override + public void afterTextChanged(Editable editable) { + surroundingsDescription = surroundings.getText().toString(); + } + }); + + + + Button submitButton=(Button) this.getView().findViewById(R.id.submit_form_button); + + View.OnClickListener listenerSubmitButton=new View.OnClickListener() { + @Override + public void onClick(View view) { + Call<Void> call = api.sendFormData(becasseauCounter, bernacheCounter, goelandCounter, mouetteCounter, pluvierCounter, cormoranCounter, + foulqueCounter, tadorneCounter, chevalierCounter, "test", zoneSelected.split("\n")[0], surroundingsDescription, "14:30"); + + call.enqueue(new Callback<Void>() { + @Override + public void onResponse(Call<Void> call, Response<Void> response) { + try { + if (response.isSuccessful()) { + Toast.makeText(getContext(), "Successful", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(getContext(), "Unsuccessful" + response.code() , Toast.LENGTH_SHORT).show(); + + new AlertDialog.Builder(getContext()) + .setTitle("Delete entry") + .setMessage(becasseauCounter + "\n" + bernacheCounter + "\n" + goelandCounter + "\n" + mouetteCounter+ "\n" + + pluvierCounter + "\n" + cormoranCounter + "\n" + foulqueCounter + "\n" + + tadorneCounter + "\n" + chevalierCounter + "\n" + "test" + "\n" + zoneSelected.split("\n")[0] + "\n" + surroundingsDescription + "\n" + "14:30") + + // Specifying a listener allows you to take an action before dismissing the dialog. + // The dialog is automatically dismissed when a dialog button is clicked. + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + // Continue with delete operation + } + }) + + // A null listener allows the button to dismiss the dialog and take no further action. + .setNegativeButton(android.R.string.no, null) + .setIcon(android.R.drawable.ic_dialog_alert) + .show(); + } + } catch (Exception e) { + Toast.makeText(getContext(), "Error: ${e.message}", Toast.LENGTH_SHORT).show(); + } + } + + @Override + public void onFailure(Call<Void> call, Throwable t) { + try { + Toast.makeText(getContext(), "Error: ${t.message}", Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + Toast.makeText(getContext(), "Error: ${e.message}", Toast.LENGTH_SHORT).show(); + } + } + }); + } + }; + + submitButton.setOnClickListener(listenerSubmitButton); } } diff --git a/app/src/main/res/layout/fragment_form.xml b/app/src/main/res/layout/fragment_form.xml index c255bdde0c891209c34e89d2dd4fccda25bce9e8..f3ff8307d02a9ec73a2e7ab97bd363eb90073bc6 100644 --- a/app/src/main/res/layout/fragment_form.xml +++ b/app/src/main/res/layout/fragment_form.xml @@ -32,7 +32,7 @@ <Spinner android:id="@+id/zone_selector" android:layout_width="match_parent" - android:layout_height="40dp" + android:layout_height="50dp" android:layout_marginTop="15dp" /> <Button @@ -45,9 +45,8 @@ android:text="@string/zone_info_text" android:textSize="12sp" /> - <!-- Title for the form --> <TextView - android:id="@+id/form_title" + android:id="@+id/surroundings" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" @@ -67,9 +66,28 @@ android:padding="20dp" android:textColor="#000000" /> - <!-- Date Picker (Optional) --> + <TextView + android:id="@+id/unknown_species" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:layout_marginTop="20dp" + android:text="@string/unknown_species_text" + android:gravity="center" + android:textColor="#333333" + android:textSize="18sp" + android:textStyle="bold" /> + + <EditText + android:id="@+id/unknown_species_input" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="15dp" + android:hint="@string/unknown_species_question" + android:padding="20dp" + android:textColor="#000000" + android:editable="false" /> - <!-- Submit Button --> <TextView android:id="@+id/form_date" android:layout_width="wrap_content" @@ -82,7 +100,6 @@ android:textStyle="bold" /> <TimePicker - android:id="@+id/time_picker" android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c745eda540a938bfb46308fc92dead537b9e5d02..e2d549b0bb4c7041081b8cb67004aeb9571e4096 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -88,6 +88,8 @@ <string name="zone_info_text">Plus d\'info sur les zones</string> <string name="corresponding_zone_text">Saisissez la zone correspondante</string> <string name="display_prediction_text">Si rien ne s\'affiche ici, c\'est que la détection d\'espèces n\'a pas fonctionné.</string> + <string name="unknown_species_text">Si rien ne s\'affiche en haut de l\'écran et que vous connaissez l\'espèce en question, écrivez cela ici. Sinon écrivez je ne sais pas.</string> + <string name="unknown_species_question">Quelle est cette espèce ?</string> <string-array name="zone_array"> <item>Zone 1</item> <item>Zone 2</item> diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6a037e25384d2dfe086b2113b911697a986fc9b0..c82554bf358bdd830134a51b133946970a712112 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,6 +11,7 @@ flexboxVersion= "3.0.0" readmoretextviewVersion= "1.0.2" litertGpu = "1.0.1" uiGraphicsAndroid = "1.7.5" +retrofit = "2.9.0" [libraries] junit = { group = "junit", name = "junit", version.ref = "junit" } @@ -24,6 +25,8 @@ flexboxlayout = {group="com.google.android.flexbox", name="flexbox", version.ref readmoretextview = {group="com.github.colourmoon", name="readmore-textview", version.ref="readmoretextviewVersion"} litert-gpu = { group = "com.google.ai.edge.litert", name = "litert-gpu", version.ref = "litertGpu" } ui-graphics-android = { group = "androidx.compose.ui", name = "ui-graphics-android", version.ref = "uiGraphicsAndroid" } +retrofit2-retrofit = {group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit"} +retrofit2-converter-gson = {group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit"} [plugins] android-application = { id = "com.android.application", version.ref = "agp" }