Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds a new Statistics Page #940

Open
wants to merge 32 commits into
base: fields_modifications
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f365ecb
intial files for statistics page
akshat22 Feb 21, 2024
1e4473f
added adapter pattern and recycler view
akshat22 Feb 29, 2024
3b3afa7
added date selection for start of season
akshat22 Mar 7, 2024
4bd67a4
resized grid to 2x4
akshat22 Mar 12, 2024
0854378
replaced date picker dialog with month and year toggle
akshat22 Mar 12, 2024
3dea62b
added dialogs and toast messages for indivdual stats
akshat22 Mar 13, 2024
1f1a057
fixed logic of 'fields' and 'entries'
akshat22 Mar 13, 2024
274f8c6
save individual statistics card
akshat22 Mar 13, 2024
34e41b1
export image with FB logo
akshat22 Mar 14, 2024
22d33ce
added snackbar
akshat22 Mar 14, 2024
5fec245
Merge branch 'main' of https://github.com/PhenoApps/Field-Book into i…
akshat22 Mar 14, 2024
2106b56
Merge branch 'main' of https://github.com/PhenoApps/Field-Book into i…
akshat22 Mar 25, 2024
0116aaa
improved UI for card view
akshat22 Mar 25, 2024
a66adf5
added calendar style heatmap dialog
akshat22 Mar 28, 2024
cb50c1d
changed horizontal heatmap dialog to vertical fragment
akshat22 Mar 30, 2024
6a2f1ad
added loading stats dialog
akshat22 Apr 3, 2024
cac3b98
replaced long press to export with a button
akshat22 Apr 3, 2024
a4a4ce9
fixed binding issue with text and highlight color
akshat22 Apr 3, 2024
bae95fe
updated dialogs and toasts
akshat22 Apr 4, 2024
2f99449
changed month number to name in month view cards
akshat22 Apr 5, 2024
9dfe91f
added scroll to first and last date functionality
akshat22 Apr 5, 2024
32bbc7e
added counter button to the heatmap
akshat22 Apr 9, 2024
64a072c
date range picker added for heatmap
akshat22 Apr 15, 2024
237ae2c
added tab layout and total section
akshat22 Apr 23, 2024
1c1b905
resolved merge conflicts
akshat22 Apr 23, 2024
65fa946
swapped field and plot icons
akshat22 Apr 23, 2024
3880c83
replaced statistics card with adapter-recycler view pattern
akshat22 Apr 24, 2024
8230658
change card title when exporting
akshat22 Apr 25, 2024
fdc6834
fixed crashing on single date selection
akshat22 Apr 30, 2024
508647a
modularized code
akshat22 Apr 30, 2024
9f80988
fixed heatmap color issue and changed icons
akshat22 Sep 12, 2024
aa449df
fixed merge conflicts
akshat22 Sep 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,13 @@ dependencies {
implementation "androidx.camera:camera-view:$camerax_version"
implementation "androidx.camera:camera-core:$camerax_version"

// The view and compose calendar library
implementation 'com.kizitonwose.calendar:view:2.5.0'
implementation 'com.kizitonwose.calendar:compose:2.5.0'

// Calendar date range picker library
implementation 'io.github.architshah248.calendar:awesome-calendar:2.0.0'

//AppIntro
implementation 'com.github.AppIntro:AppIntro:6.3.1'

Expand Down
38 changes: 14 additions & 24 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@
android:name=".application.FieldBook"
android:hardwareAccelerated="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher"
android:label="@string/field_book"
android:largeHeap="true"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher"
android:theme="@style/BaseAppTheme"
android:usesCleartextTraffic="true"
tools:ignore="GoogleAppIndexingWarning"
tools:replace="android:label">

<activity
android:name=".activities.ScannerActivity"
android:name=".activities.StatisticsActivity"
android:exported="false" />

<!-- The Main Page activity that lists all other activities -->
<activity
android:name=".activities.ScannerActivity"
android:exported="false" /> <!-- The Main Page activity that lists all other activities -->
<activity
android:name=".activities.ConfigActivity"
android:configChanges="orientation|keyboardHidden|screenSize|locale"
Expand All @@ -32,11 +32,10 @@
android:targetActivity=".activities.ConfigActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<!-- - File Import activity (field manager, local and brapi) -->
</activity> <!-- - File Import activity (field manager, local and brapi) -->
<activity
android:name=".activities.FieldEditorActivity"
android:alwaysRetainTaskState="false"
Expand All @@ -62,12 +61,10 @@
<activity
android:name=".activities.CollectActivity"
android:configChanges="keyboardHidden|orientation|screenSize|locale|keyboard"
android:exported="true"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:exported="true"
android:windowSoftInputMode="adjustPan|stateHidden" />

<!-- Data grid is available through the collect activity, once the setting is set -->
android:windowSoftInputMode="adjustPan|stateHidden" /> <!-- Data grid is available through the collect activity, once the setting is set -->
<activity
android:name=".activities.DataGridActivity"
android:configChanges="orientation|keyboardHidden|screenSize|locale"
Expand All @@ -83,9 +80,7 @@
android:name=".activities.PreferencesActivity"
android:configChanges="orientation|keyboardHidden|screenSize|locale"
android:launchMode="singleTop"
android:screenOrientation="portrait" />

<!-- About activity uses a library, which uses material design components, it has its own style file -->
android:screenOrientation="portrait" /> <!-- About activity uses a library, which uses material design components, it has its own style file -->
<!-- shows version information and references to other apps, etc. -->
<activity
android:name=".activities.AboutActivity"
Expand Down Expand Up @@ -169,18 +164,13 @@
android:name="com.journeyapps.barcodescanner.CaptureActivity"
android:screenOrientation="portrait"
android:stateNotNeeded="true"
tools:replace="android:screenOrientation" />

<!-- used to define SAF storage directory, required at start of app -->
tools:replace="android:screenOrientation" /> <!-- used to define SAF storage directory, required at start of app -->
<activity
android:name=".activities.DefineStorageActivity"
android:theme="@style/AppTheme" />

<!-- found in preferences, used to select a TTS language locale -->
android:theme="@style/AppTheme" /> <!-- found in preferences, used to select a TTS language locale -->
<activity
android:name=".activities.LocaleChoiceActivity"
android:theme="@style/AppTheme" />

<activity
android:name=".activities.CameraActivity"
android:exported="true"
Expand Down Expand Up @@ -215,6 +205,7 @@
<action android:name="android.intent.action.GET_CONTENT" />
</intent>
</queries>

<supports-screens
android:anyDensity="true"
android:largeScreens="true"
Expand All @@ -228,6 +219,7 @@
<uses-feature
android:name="android.hardware.camera"
android:required="false" />

<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
Expand All @@ -246,9 +238,7 @@
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.fieldbook.tracker.R;
import com.fieldbook.tracker.adapters.ImageListAdapter;
import com.fieldbook.tracker.database.DataHelper;
import com.fieldbook.tracker.database.models.ObservationModel;
import com.fieldbook.tracker.database.models.ObservationUnitModel;
import com.fieldbook.tracker.fragments.ImportDBFragment;
import com.fieldbook.tracker.objects.FieldObject;
Expand Down Expand Up @@ -293,9 +294,9 @@ private void loadScreen() {
settingsList = findViewById(R.id.myList);

String[] configList = new String[]{getString(R.string.settings_fields),
getString(R.string.settings_traits), getString(R.string.settings_collect), getString(R.string.settings_export), getString(R.string.settings_advanced), getString(R.string.about_title)};
getString(R.string.settings_traits), getString(R.string.settings_collect), getString(R.string.settings_export), getString(R.string.settings_advanced), getString(R.string.settings_statistics), getString(R.string.about_title)};

Integer[] image_id = {R.drawable.ic_nav_drawer_fields, R.drawable.ic_nav_drawer_traits, R.drawable.ic_nav_drawer_collect_data, R.drawable.trait_date_save, R.drawable.ic_nav_drawer_settings, R.drawable.ic_tb_info};
Integer[] image_id = {R.drawable.ic_nav_drawer_fields, R.drawable.ic_nav_drawer_traits, R.drawable.ic_nav_drawer_collect_data, R.drawable.trait_date_save, R.drawable.ic_nav_drawer_settings, R.drawable.ic_nav_drawer_statistics, R.drawable.ic_tb_info};

settingsList.setOnItemClickListener((av, arg1, position, arg3) -> {
Intent intent = new Intent();
Expand Down Expand Up @@ -325,6 +326,13 @@ private void loadScreen() {
startActivity(intent);
break;
case 5:
if (checkObservationsExist() > 0) {
intent.setClassName(ConfigActivity.this,
StatisticsActivity.class.getName());
startActivity(intent);
}
break;
case 6:
intent.setClassName(ConfigActivity.this,
AboutActivity.class.getName());
startActivity(intent);
Expand Down Expand Up @@ -377,6 +385,19 @@ private int checkTraitsExist() {
return 1;
}

/**
* Checks if any observations are collected.
* @return -1 if there are no observations, else 1
*/
private int checkObservationsExist() {
final ObservationModel[] observations = database.getAllObservations();
if (observations.length == 0) {
Utils.makeToast(getApplicationContext(), getString(R.string.warning_no_observations));
return -1;
}
return 1;
}

// Helper function to merge arrays
String[] concat(String[] a1, String[] a2) {
String[] n = new String[a1.length + a2.length];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package com.fieldbook.tracker.activities;

import android.app.AlertDialog;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Menu;
import android.view.MenuItem;

import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.fieldbook.tracker.R;
import com.fieldbook.tracker.adapters.StatisticsAdapter;
import com.fieldbook.tracker.database.DataHelper;
import com.fieldbook.tracker.database.models.ObservationModel;
import com.fieldbook.tracker.dialogs.StatisticsCalendarFragment;
import com.google.android.material.tabs.TabLayout;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.inject.Inject;

import dagger.hilt.android.AndroidEntryPoint;

@AndroidEntryPoint
public class StatisticsActivity extends ThemedActivity {
public static String TAG = "Statistics Activity";
@Inject
DataHelper database;
List<String> seasons = new ArrayList<>();
RecyclerView rvStatisticsCard;
private ToggleVariable toggleVariable = ToggleVariable.TOTAL;
AlertDialog loadingDialog;
public enum ToggleVariable {
TOTAL,
YEAR,
MONTH
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_statistics);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

if (getSupportActionBar() != null) {
getSupportActionBar().setTitle(getString(R.string.settings_statistics));
getSupportActionBar().getThemedContext();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
}

rvStatisticsCard = findViewById(R.id.statistics_card_rv);
rvStatisticsCard.setLayoutManager(new LinearLayoutManager(this));

TabLayout tabLayout = findViewById(R.id.tab_layout);

tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.stats_tab_layout_total)));
tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.stats_tab_layout_year)));
tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.stats_tab_layout_month)));

tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
switch (tab.getPosition()) {
case 0: toggleVariable = ToggleVariable.TOTAL; break;
case 1: toggleVariable = ToggleVariable.YEAR; break;
case 2: toggleVariable = ToggleVariable.MONTH; break;
}
loadData();
}

@Override
public void onTabUnselected(TabLayout.Tab tab) {

}

@Override
public void onTabReselected(TabLayout.Tab tab) {

}
});

AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.AppAlertDialog);
builder.setView(getLayoutInflater().inflate(R.layout.dialog_loading, null));
loadingDialog = builder.create();

loadData();

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_statistics, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {

final int heatmapId = R.id.stats_heatmap;

int itemId = item.getItemId();

if (itemId == heatmapId) {
StatisticsCalendarFragment calendarFragment = new StatisticsCalendarFragment(this);
getSupportFragmentManager().beginTransaction().replace(android.R.id.content, calendarFragment).addToBackStack(null).commit();
} else if (itemId == android.R.id.home) {
onBackPressed();
}
return super.onOptionsItemSelected(item);
}

@NonNull
public DataHelper getDatabase() {
return database;
}

/**
* Displays the loading screen and loads the statistics asynchronously
*/
public void loadData() {
loadingDialog.show();

Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(this::setSeasons);
}

public void setSeasons() {

if (toggleVariable == ToggleVariable.TOTAL) {
seasons.clear();
seasons.add("");
} else {
Set<String> uniqueSeasons = new TreeSet<>(Comparator.reverseOrder());

ObservationModel[] observations = database.getAllObservations();
for (ObservationModel observation : observations) {
String timeStamp = observation.getObservation_time_stamp();
if (toggleVariable == ToggleVariable.YEAR)
uniqueSeasons.add(timeStamp.substring(0, 4));
else
uniqueSeasons.add(timeStamp.substring(0, 7));
}

seasons = new ArrayList<>(uniqueSeasons);
}
rvStatisticsCard.setAdapter(new StatisticsAdapter(this, seasons, toggleVariable));

// Dismiss the dialog after the recycler view loads all its children
rvStatisticsCard.post(() -> loadingDialog.dismiss());
}

}
Loading