I apologize if my code seems messy, I am fairly new to the whole app development game. I am trying to build an app as a university project, where one of its many features is detecting the surrounding noise level of the user in decibels, in order to determine if he / she is socializing with other people (silly, I know). I am working with a Bottom Navigation Menu, and 5 different Fragments, one of which contains a button which will start the sound measurement process.
This is the aforementioned Fragment's code:
package com.example.ssresilience;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.media.MediaRecorder;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentResultListener;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import org.w3c.dom.Text;
import java.io.IOException;
public class MeasureFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private String goal;
private Button fg_measure_button_go;
private MediaRecorder mRecorder = null;
public MeasureFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment MeasureFragment.
*/
// TODO: Rename and change types and number of parameters
public static MeasureFragment newInstance(String param1, String param2) {
MeasureFragment fragment = new MeasureFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_measure, container, false);
// Retrieve the selected Goal from the DataSite Class
goal = ((DataSite)getActivity().getApplication()).getGoal();
// Initialize the Fragment's TextViews
TextView fg_measure_title = (TextView)rootView.findViewById(R.id.fg_measure_title);
TextView fg_measure_text = (TextView)rootView.findViewById(R.id.fg_measure_text);
Button fg_measure_button_go = (Button)rootView.findViewById(R.id.fg_measure_button_go);
fg_measure_button_go.setOnClickListener(this::onClick);
// Fill the Fragment's TextViews according to the selected Goal
if (goal != null) {
if (goal.equals("socialize")) {
fg_measure_title.setText("Socialize More");
fg_measure_text.setText("Measure the level of noise of your surrounding environment, in order to let the app know you're socializing with people.");
}
if (goal.equals("study")) {
fg_measure_title.setText("Enhance Study Motives");
fg_measure_text.setText("Allow the app to calculate and determine your stress level, by taking the Stress Test. Remember, a higher stress level leads to lower study motives.");
}
if (goal.equals("exercise")) {
fg_measure_title.setText("Physical Exercise");
fg_measure_text.setText("Measure the level of your current physical activity state by utilizing your device's accelerometer sensor.");
}
}
return rootView;
}
@SuppressLint({"UseCompatLoadingForDrawables", "ResourceAsColor", "UseCompatLoadingForColorStateLists", "NonConstantResourceId", "SetTextI18n"})
public void onClick(View v) {
switch (v.getId()) {
case R.id.fg_measure_button_go:
((SoundMeter) getActivity().getApplication()).getAmplitude();
break;
}
}
}
At the bottom of the file, there's the line of code ((SoundMeter) getActivity().getApplication()).getAmplitude();. This calls the getAmplitude() method from the SoundMeter activity.
Below is the code for SoundMeter.java. I found this while searching online, and apparently it works for many users.
package com.example.ssresilience;
import android.app.Application;
import android.media.MediaRecorder;
import java.io.IOException;
public class SoundMeter extends Application {
static final private double EMA_FILTER = 0.6;
private MediaRecorder mRecorder = null;
private double mEMA = 0.0;
public void start() {
if (mRecorder == null) {
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mRecorder.setOutputFile("/dev/null/");
try {
mRecorder.prepare();
} catch (IllegalStateException | IOException e) {
e.printStackTrace();
}
mRecorder.start();
mEMA = 0.0;
}
}
public void stop() {
if (mRecorder != null) {
mRecorder.stop();
mRecorder.release();
mRecorder = null;
}
}
public double getTheAmplitude(){
if(mRecorder != null)
return (mRecorder.getMaxAmplitude());
else
return 1;
}
public double getAmplitude() {
if (mRecorder != null)
return (mRecorder.getMaxAmplitude()/2700.0);
else
return 0;
}
public double getAmplitudeEMA() {
double amp = getAmplitude();
mEMA = EMA_FILTER * amp + (1.0 - EMA_FILTER) * mEMA;
return mEMA;
}
}
Lastly, in my AndroidManifest.xml file, I have included these 2 lines of code:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
and
<activity
android:name=".SoundMeter"
tools:ignore="Instantiatable" />
<activity
The problem is: as soon as I press the button in MeasureFragment, which calls the getAmplitude() method, the app instantly crashes, without any output in the Logcat. I can confirm that the technique I use to call the Activity method from the current Fragment (((SoundMeter) getActivity().getApplication()).getAmplitude();) is not the issue, since I use it in other occasions as well, and it works flawlessly.
I am aware I need to store the retrieved value to a variable, but in order to do that I need to get the process going first, and I cannot do that when the app crashes.
Any idea as to what I am doing wrong, or something that can be changed in the code?
Thank you in advance!