Moving logic from MainActivity to another class in Android - java

I have a simple logic on my app that looks for a certain pitch.
The problem is that the logic is in the OnCreate method of the app (it has to detect the pitch the moment the application is running).
It is a little bit ugly as I plan to add some more logic as the application starts.
Does anyone have any advice of how to move that code to a different class so that it could be invoked from there?
The class still has to access views in the main activity.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AudioDispatcher dispatcher = AudioDispatcherFactory.fromDefaultMicrophone(22050,1024,0);
dispatcher.addAudioProcessor(new PitchProcessor(PitchEstimationAlgorithm.FFT_YIN, 22050, 1024, new PitchDetectionHandler() {
#Override
public void handlePitch(PitchDetectionResult pitchDetectionResult,
AudioEvent audioEvent) {
final float pitchInHz = pitchDetectionResult.getPitch();
runOnUiThread(new Runnable() {
#Override
public void run() {
Float value = pitchInHz;
Toast.makeText(getApplicationContext(),value.tostring(), Toast.LENGTH_SHORT).show();
}
});
}
}));
foo = new Thread(dispatcher,"Audio Dispatcher");
foo.start();
}

Basically, you have two options to make your code cleaner.
Move all the code in onCreate() (except first two lines) into another method, let's say lookForPitch(). Then you can call it right in onCreate().
If you plan to create more methods that focus on audio processing, you can create separate class, for example AudioUtils.java. This util class should contain public static methods, that you can invoke from any place in your code. In case of onCreate() you may call it like this: AudioUtils.lookForPitch(). Also if you want to handle Views, that are accessible only in your Activity, you can pass them as argument. So your method in AudioUtils can look like this:
public static void lookForPitch(TextView myTextView) {
// your code goes here
}

Just make it a method
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myLongAndSweetMethod();
}
private void myLongAndSweetMethod(){
AudioDispatcher dispatcher = AudioDispatcherFactory.fromDefaultMicrophone(22050,1024,0);
dispatcher.addAudioProcessor(new PitchProcessor(PitchEstimationAlgorithm.FFT_YIN, 22050, 1024, new PitchDetectionHandler() {
#Override
public void handlePitch(PitchDetectionResult pitchDetectionResult,
AudioEvent audioEvent) {
final float pitchInHz = pitchDetectionResult.getPitch();
runOnUiThread(new Runnable() {
#Override
public void run() {
Float value = pitchInHz;
Toast.makeText(getApplicationContext(),value.tostring(), Toast.LENGTH_SHORT).show();
}
});
}
}));
foo = new Thread(dispatcher,"Audio Dispatcher");
foo.start();
}
Then using the Code folding of Android Studio to hide it.

If you want to improve the readability of your code I can recommend the book "Clean Code: A Handbook of Agile Software Craftmanship" by Robert C. Martin (aka Uncle Bob).
This book is really great! It helped me to a lot to make my code more clean and easy to read. It is a book you should have read if you want to be(come) a professional software developer.

Related

android - set intent extra from inside runnable

I'm still new to android development. I've been at this problem for some time but am still unable to figure out what to do on my own. In an Activity I set up a series of Runnables containing CountDownTimers. One executes after the next, but depending on which CountDownTimer is active, I need to pass a different Intent.extra to a fragment. I've tried setting my extra from inside Runnable, inside Run, and inside of the CountDownTimer onTick, and onFinish.
I fear I have way too much going on in my original Activity to post it, but here is the problem in essence.
public class MatchUpActivity extends Activity implements OpponentFragment.OnFragmentInteractionListener{
List mTotalDrafts;
Bundle mBundle;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_match_up);
mBundle = new Bundle();
mDraftUsernames = extras.getStringArrayList("DRAFT_LIST");
for (int i = 0; i < totalDrafts; i++) {
Handler delayhandler = new Handler();
delayhandler.postDelayed(new Runnable() {
//bundle.put("extra", totalDrafts.get(0))
public void run() {
//bundle.put("extra", totalDrafts.get(0))
getTimer();
}
}, mTodaysDraftTime + (i * singleDraftDuration) - Calendar.getInstance().getTimeInMillis());
}
}
CountDownTimer
private void getTimer() {
new CountDownTimer(singleDraftDuration, 1000) {
public void onTick(long millisUntilFinished) {
//bundle.put("extra", totalDrafts.get(0))
}
public void onFinish() {
//bundle.put("extra", totalDrafts.get(0))
list.remove(0)
}
}.start();
}
}
I am able to remove items from my list in onFinish, but after I do so I need to send the next element in the list as an extra.
I hope this is enough code to get my question across. I tried to change some things from my original code for simplicity. If there is something I am missing or a better way to do this, please, anybody let me know.
Define the Bundle as global variable in your Activity and not in a Method implementation.

The method Sleep(int) is undefined for the type new Runnable(){} in Android

I have used code from Android recipe - create loading screen However, I am having problems using this.sleep() as I receive the error "The method Sleep(int) is undefined for the type new Runnable(){}" Other questions have stated that there is a problem with naming the class such as "Thread". This does not seem to be the case for my code though.
Code
public class LoadingScreenActivity extends Activity {
//Introduce an delay
private final int WAIT_TIME = 2500;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
System.out.println("LoadingScreenActivity screen started");
setContentView(R.layout.loading_screen);
findViewById(R.id.mainSpinner1).setVisibility(View.VISIBLE);
new Handler().postDelayed(new Runnable(){
#Override
public void run() {
//Simulating a long running task
this.Sleep(1000);
System.out.println("Going to Profile Data");
/* Create an Intent that will start the ProfileData-Activity. */
Intent mainIntent = new Intent(LoadingScreenActivity.this,ProfileData.class);
LoadingScreenActivity.this.startActivity(mainIntent);
LoadingScreenActivity.this.finish();
}
}, WAIT_TIME);
}
}
What you're probably looking for is
Thread.sleep(1000);
Runnable is an Interface and does not have any implemented methods of it's own, only public void run(), which you have to override and implement.
That code is simply wrong. It should be Thread.sleep(1000), but even so, it's not correct, and it will produce an "Application not responding" error, because it's trying to pause the UI thread.
Please take some time and read a Java and Android book. You won't get anywhere following half-baked implementations from the Internet.

Saving data across rotation does not seem to work in my android project

PLESAE NOTE: The solution to my problem is in bold text at the bottom. I accepted Melquiades's answer because he helped me filter out everything that could have been the problem. It turns out, I was the problem, not android or anything else. So if you are looking for the answer, read below.
I'm trying to save the state of a variable as it is before onPause(); , onStop(); , onDestroy(); are called.
The book I am using has me override a method called
public void onSaveInstanceState(Bundle savedInstanceState){
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putInt(KEY_INDEX, myIntVaraible);
}
the variables you see in the parameter are declared at the beginning of the class
private static final String KEY_INDEX = "index";
private int myIntVariable;
with this method created, the book tells me to then go the the onCreate method and add
if(savedInstanceState != null){
myIntVariable = savedIntanceState.getInt(KEY_INDEX, 0);
}
But this does not work.
Whenever the activity is destroyed and created, the myIntVariable is reset to 0.
What I did to fix this is I went to my manifest file and
added android:configChanges="orientation|screenSize".
However, I have read that this is not practical and is strongly advised against.
EDIT: As suggested, I am adding my onCreate(); and onResume(); methods..
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate()");
setContentView(R.layout.activity_main);
iterateQuestions();
mTrueButton = (Button)findViewById(R.id.trueBt);
mTrueButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
correctPressed = true;
checkForTrue();
}
});
mFalseButton = (Button)findViewById(R.id.falseBt);
mFalseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
falsePressed = false;
checkForFalse();
}
});
mNextButton = (ImageButton)findViewById(R.id.nextBt);
mNextButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
try{
mIndexCounter++;
mTextViewQuestion = (TextView)findViewById(R.id.text_question_view);
int QuestionToShow = mQuestionBank[mIndexCounter].getQuestion();
mTextViewQuestion.setText(QuestionToShow);
}
catch(Exception e){
iterateQuestions();
}
}
});
mTextViewQuestion = (TextView)findViewById(R.id.text_question_view);
mTextViewQuestion.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
try{
mIndexCounter++;
mTextViewQuestion = (TextView)findViewById(R.id.text_question_view);
int QuestionToShow = mQuestionBank[mIndexCounter].getQuestion();
mTextViewQuestion.setText(QuestionToShow);
}
catch(Exception e){
iterateQuestions();
}
}
});
mPrevButton = (ImageButton)findViewById(R.id.prevBtn);
mPrevButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
try{
mIndexCounter--;
mTextViewQuestion = (TextView)findViewById(R.id.text_question_view);
int QuestionToShow = mQuestionBank[mIndexCounter].getQuestion();
mTextViewQuestion.setText(QuestionToShow);
}catch(Exception e){
iterateQuestionsReverse();
}
}
});
}
and
#Override
public void onResume(){
super.onResume();
Log.d(TAG,"onResume()");
}
For all intents and purposes, the variable mIndexCounter is the "myIntVariable" I mentioned.
SOLUTION: I was using a book and unfortunately, since I am new to android programming, relied too much on the code written in the book. The authors usually add new code in their book as bold, black text. This time, they failed to do that and I had trouble figuring out why my data was not being saved. It turns out that it was saved all along, I just failed to update the view with the saved data whenever it was retrieved. After adding 3 lines of simple code, my mistake was obvious and the goal I had been trying to accomplish, a success.
My program displayed a string of text that was dependant on an int that was used to retrieve information from the R.java class. After launching the app, when the user presses Next, the data changes because the int is incremented and the String on the view changes.
This data was to be saved due to the nature of android destroying any unsaved data upon orientation change.
All I had to do was simply add the saved data, an int, the same way I used to display this string/text in the first place. Instead, I foolishly assumed it would do it automatically because the book did not add this code and I relied on it too much.
This was a great learning experience and if anyone ever comes across something like this, feel free to email me if my answer is not clear.
Instead of in onCreate(), restore your variable in onRestoreInstanceState():
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
myIntVariable = savedIntanceState.getInt(KEY_INDEX, 0);
}
The docs also say:
Note: Because onSaveInstanceState() is not guaranteed to be called, you should use it only to record the transient state of the activity (the state of the UI)—you should never use it to store persistent data. Instead, you should use onPause() to store persistent data (such as data that should be saved to a database) when the user leaves the activity.
Btw, change your onCreate() signature to:
#Override
protected void onCreate(Bundle savedInstanceState) {
as in the docs.
Try this:
android:configChanges="keyboard|keyboardHidden|orientation"
As stated here

findViewById(R.id.progressbar) and static hell:

clipProgress resets a progress bar to zero:
public class Whatever extends Activity implements ListenerA, ListenerB {
private Handler mHandler = new Handler();
ProgressBar myProgressBar;
int myProgress = 0;
public void clipProgress() {
myProgressBar = (ProgressBar)findViewById(R.id.progressbar);
myProgressBar.setProgress(myProgress);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
clipProgress();
public void onClick(View view) {
Duration.duration = 0;
startRecording();
new Thread(new Runnable(){
#Override
public void run() {
while(myProgress<100){
try{
myHandle.sendMessage(myHandle.obtainMessage());
Thread.sleep(50);
}catch(Throwable t){
}
}
}
}).start();
}
Handler myHandle = new Handler(){
#Override
public void handleMessage(Message msg) {
myProgress++;
myProgressBar.setProgress(myProgress);
}
};
});
}
I have two different methods, below, where I suppose I could attempt to reset the progress bar to zero. But they both reside in a different class entirely:
#Override protected void onPostExecute(Void result) {
Whatever.clipProgress(); //...which throws all kinds of red lines if I use this statement.
}
and
public void stopThis(){
thing.stop();
try {
Whatever.clipProgress(); //...which throws all kinds of red lines if I use this statement.
dos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Eclipse wants to apply "static" to various initializations in the Whatever class. But those fixes just leave findViewById(R.id.progressbar) redlined.
I just want to reset the progress bar (which was started from within class 1) from class 2. It's too much to ask for.
UPDATE:
Whatever is the starting point, the Android Activity. Its handler, which I should have included in the above snip, right underneath onCreate is included in the snip below. So more completely, and invoked from a button, is thus:
public void onClick(View view) {
new Thread(new Runnable(){
#Override
public void run() {
while(myProgress<100){
try{
myHandle.sendMessage(myHandle.obtainMessage());
Thread.sleep(87);
}catch(Throwable t){
}
}
}
}).start();
}
Handler myHandle = new Handler(){
#Override
public void handleMessage(Message msg) {
myProgress++;
myProgressBar.setProgress(myProgress);
}
};
Once that button is pressed a listener is produced:
myTask = new Task();
myTask.addTaskListener(this);
myTask.execute(this);
It is inside the Task class:
public class Task extends AsyncTask<Whatever, ArrayList<Float>, Void>{
...
public void addTaskListener(TaskListener rl){
this.rl = rl;
}
...
that those two functions reside, one of which I'm hoping to use to stop the progress bar.
A task is designed elsewhere to shut down at exactly a constant number of seconds, and so I can hardcode 50 into that progress bar value on the sleep(). I just want to be able to shut the bar down back to zero earlier if I stop the task earlier.
Eclipse warns you because you are accessing the clipProgress method in a static way (by calling Whatever.clipProgress()).
Also, you should only assign your progress bar once, so move
myProgressBar = (ProgressBar)findViewById(R.id.progressbar);
into your onCreate method.
You should probably pass the ProgressBar into the other class that needs to update it, and then do something like
myProgressBar.post(new Runnable() {
public void run() {
myProgressBar.setProgress(myProgress);
}
});
this post method updates the ProgressBar on the UI thread, which is the only safe way to do it. Make sure you are only doing any of this if the activity with the ProgressBar is currently active.
You should also question whether you need to update the progress bar in this other class at all. I only see you updating the myProgress variable inside your handler, where you set the progress on the bar immediately after. Could you explain a bit more about the relationship between this other class and the activity with your progress bar?
This has more to do with Java programming and instance / static methods than with Android:
When you call Whatever.clipProgress() you're attempting to make a call to a static method call to clipProgress on the Whatever class, which is why Eclipse wants to add the static modifier.
However, findViewById() is an instance method (read the link above) on Activity, which is presumably some ancestor of your Whatever class. Static methods can't call instance methods statically, so even if you let Eclipse make clipProgress static, you need to call clipProgress on an instance of your activity.
Your other class needs to have a reference to the instance of the class on which you want to call clipProgress(). You should either pass in that reference to the other class, or as another poster noted, pass in a reference to the ProgressBar itself.
Then you can call e.g. mWhatever.clipProgress() or progressBar.setProgress(0).

I have one Activity. In this Activity, I executed a AsyncTask , but it's not working

private class ExecuteLocations extends AsyncTask<String, Void, Void>{
private final ProgressDialog dialog = new ProgressDialog(ListProfiles.this);
protected void onPreExecute() {
//this.dialog.setMessage("Starting pre-execute...");
//this.dialog.show();
}
#Override
protected Void doInBackground(String... arg0) {
check_profiles_lm=(LocationManager) ListProfiles.this.getSystemService(LOCATION_SERVICE);
myLocListen = new LocationListener(){
#Override
public void onLocationChanged(Location location) {
HashMap params = new HashMap();
params.put("lat", Double.toString(location.getLatitude()));
params.put("long", Double.toString(location.getLongitude()));
postData("http://mydomain.com",params);
}
#Override
public void onStatusChanged(String provider, int status,Bundle extras) {
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onProviderEnabled(String provider) {
}
};
check_profiles_lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 30000, 0, myLocListen);
return null;
}
protected void onPostExecute(final Void unused) {
if (this.dialog.isShowing()) {
//this.dialog.dismiss();
}
//Do something else here
}
}
Basically, my objective is:
Constantly post the latitude/longitude to my website every minute or so.
Of course, I want to do this in another thread, so it doesn't mess up my UI thread. This AsyncTask is supposed to solve that...but it's bringing up an error and I don't know why.
What can you do to accomplish my objective? It's very simple...just scan location and post to web every minute, in the background.
By the way, my onCreate method is like this. That's basically it.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new ExecuteLocations().execute();
setContentView(R.layout.main_list);
}
Break it into 3 steps:
Make whatever you want to work work w/o AsyncTask
Make sure you've figured out AsyncTask, do a simple Log.e("haha", "gotcha") in doInBackground(...) and check that it shows
Combine the two.
For this case, I'd probably go with a Service triggered by AlarmManager. Guess you'll need the latter anyways.
Create this as a Service. No need to have a Timer or AlarmManager as you register for location events and act appropriately.
An example of listening to location events can be found at
http://code.google.com/p/android-bluetooth-on-motion/source/browse/#svn/trunk/BluetoothOnMotion/src/com/elsewhat
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, MIN_TIME_NETWORK, MIN_DISTANCE_NETWORK,locationListener);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME_GPS, MIN_DISTANCE_GPS,locationListener);

Categories