This is my first Android/Java app. I am using the first answer here to try to initiate a repeating task, updating a seekbar ("timeSlider") to show progress as an audio file plays. Here is my code (eliminating a few irrelevant lines):
private int timeSliderInterval = 1000; // 1 second
private Handler timeSliderHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play);
Intent intent = getIntent();
Runnable doUpdateTimeSlider = new Runnable() {
#Override
public void run() {
timeSliderHandler.postDelayed(doUpdateTimeSlider, timeSliderInterval);
updateTimeSlider();
}
};
void startUpdateTimeSlider() {
doUpdateTimeSlider.run();
}
void stopUpdateTimeSlider() {
timeSliderHandler.removeCallbacks(doUpdateTimeSlider);
}
final SeekBar timeSlider = (SeekBar) findViewById(R.id.timeSlider);
if (timeSlider != null) {
timeSliderHandler = new Handler();
startUpdateTimeSlider();
}
#Override
public void onDestroy() {
super.onDestroy();
stopUpdateTimeSlider();
}
The project does not display in the emulator. Tooltips show these errors:
In addition, the startUpdateTimeSlider and stopUpdateTimeSlider functions are showing this error in tooltips:
Also, in the Run window, I'm getting:
emulator: emulator window was out of view and was recentered
emulator: ERROR: _factory_client_recv: Unknown camera factory query
name in ' '
Any help will be greatly appreciated!
First issue is self explained, you need to add final modifier.
final Runnable doUpdateTimeSlider = ...
Second one - move startUpdateTimerSlider() method, now its inside onCreate() method
Looks like you missed }:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play);
Intent intent = getIntent();
Runnable doUpdateTimeSlider = new Runnable() {
#Override
public void run() {
timeSliderHandler.postDelayed(doUpdateTimeSlider, timeSliderInterval);
updateTimeSlider();
}
};
}//<--------HERE
void startUpdateTimeSlider() {
doUpdateTimeSlider.run();
}
Related
I've created a splash screen in my app that displays for 3 seconds before moving to LogInActivity. It looks like this:
public class SplashScreenActivity extends AppCompatActivity {
// Splash screen displays for 3 seconds.
private static int SplashTimer = 3000;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_screen);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
finishTiming();
}
public void finishTiming() {
Handler h = new Handler(Looper.getMainLooper());
h.postDelayed(new Runnable() {
#Override
public void run() {
// App then transitions to login activity.
Intent intent = new Intent(SplashScreenActivity.this, LogInActivity.class);
startActivity(intent);
finish();
}
}, SplashTimer);
}
}
Now I'm trying to test it and I have two tests. testTimer() checks that the timer works as it should and SplashScreenToLogin() checks that the activity moves to the LogInActivity next:
#RunWith(AndroidJUnit4.class)
public class SplashScreenActivityInstrumentationTest {
SplashScreenActivity splashScreenActivity;
private Instrumentation.ActivityMonitor activityMonitor;
#Before
public void createSplashScreenActivity() {
splashScreenActivity = new SplashScreenActivity();
activityMonitor = InstrumentationRegistry.getInstrumentation().addMonitor(LogInActivity.class.getName(), null, false);
}
#Test
public void testTimer() {
assertFalse(splashScreenActivity.isFinishing());
splashScreenActivity.finishTiming();
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
assertTrue(splashScreenActivity.isFinishing());
}
#Test
public void SplashScreenToLogin() {
splashScreenActivity.finishTiming();
splashScreenActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
}
});
LogInActivity logInActivity = (LogInActivity) InstrumentationRegistry.getInstrumentation().waitForMonitorWithTimeout(activityMonitor, 10000);
assertNotNull(logInActivity);
logInActivity.finish();
}
}
My issues is that when I run this I get the following error: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
This error displays at the first line of each test function and I'm not sure why. Can someone explain why it's happening and a potential solution?
I'm trying to set the text of a TextView in my Android app using the following function:
#Override
public void onOSSubscriptionChanged(OSSubscriptionStateChanges stateChanges) {
if (!stateChanges.getFrom().getSubscribed() && stateChanges.getTo().getSubscribed()) {
new AlertDialog.Builder(this)
.setMessage("You have successfully subscribed to push notifications!")
.show();
// Get player ID and output to Main Activity
TextView playerIdView = findViewById(R.id.playerIdView);
playerIdView.setText(stateChanges.getTo().getUserId());
}
Log.i("Debug", "onOSPermissionsChanged: " + stateChanges);
}
This uses the OneSignal API to get the user's unique ID, which is returned as a string. After some debugging I realised the contents of a TextView can't be changed outside of the onCreate() method. However, the stateChanges parameter is required, which only exists within onOSSubscriptionChanged. Is there any way of getting around this?
EDIT: the error was elsewhere. stateChanges.getTo().getUserId() was returning null.
You need to set it on UI thread
playerIdView.post(new Runnable() {
public void run() {
playerIdView.setText(stateChanges.getTo().getUserId());
}
});
or
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = new Runnable() {
#Override
public void run() {
playerIdView.setText(stateChanges.getTo().getUserId());}
};
mainHandler.post(myRunnable);
you need to initialize you textview in your onCreateView() method and after that you can use that textView pretty much anywhere as long as you are in UI thread. So change your code to below:
Declare your textview globally so that you can use it anywhere in your activity instance.
TextView playerIdView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
playerIdView = findViewById(R.id.playerIdView);
}
and then in your onSSubscription method just do the following:
#Override
public void onOSSubscriptionChanged(OSSubscriptionStateChanges stateChanges) {
if (!stateChanges.getFrom().getSubscribed() && stateChanges.getTo().getSubscribed()) {
new AlertDialog.Builder(this)
.setMessage("You have successfully subscribed to push notifications!")
.show();
// Get player ID and output to Main Activity
playerIdView.setText(stateChanges.getTo().getUserId());
}
Log.i("Debug", "onOSPermissionsChanged: " + stateChanges);
}
Try This
runOnUiThread(new Runnable(){
#override
public void run() {
playerIdView.setText(stateChanges.getTo().getUserId());
}
})
I have spent almost the day trying to figure it out and yet could not manage to achieve it. I basically have two activities, MainActivity.java and GameActivity.java. The music stops but after going back to MainActivity.java and coming back to GameActivity.java using ImageView several times.
MainActivity.java
public class MainActivity extends AppCompatActivity {
ImageView iv;
public static final String BG_SOUND_CHECK = "background playing!";
// public static MediaPlayer backgroundSound;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Handler handler = new Handler();
final MediaPlayer backgroundSound = MediaPlayer.create(this, R.raw.background_music);
handler.postDelayed(new Runnable() {
#Override
public void run() {
backgroundSound.setLooping(true);
backgroundSound.start();
Log.v(BG_SOUND_CHECK, "After loop Started!");
}
}, 3000);
getSupportActionBar().hide();
iv = (ImageView)findViewById(R.id.playImage);
iv.setOnClickListener(new View.OnClickListener(){
public void onClick(View view1){
// backgroundSound.setLooping(false);
backgroundSound.pause();
// Log.v(BG_SOUND_CHECK, "Playing After Play Button Click");
Intent myIntent = new Intent(MainActivity.this, GameActivity.class);
// myIntent.putExtra("backgroundSoundObj",backgroundSound);
startActivity(myIntent);
// Log.v(BG_SOUND_CHECK, "After Game Scene");
}
});
}
}
GameActivity.java
public class GameActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
// MainActivity.backgroundSound.pause();
// Rest of the code
}
}
I tried putting the music onPause(); in
1) ImageView onClick();
2) As soon as the GameActivity.java is loaded
but both ways end up pausing it only after I try coming back to MainActivity.java and going to GameActivity.java several times.
Do you see how you put your backgroundSound.start() inside the postDelayed-3000 ?
backgroundSound.pause() anytime before that 3000ms is not going to do anything.
It has nothing to do with going back and forth between your two activities, it just so happens that doing that takes up more than 3000ms, so when you do click for pause(), 3000ms has passed.
What you can do, to fit your current code, is to separate that into a Runnable.
Runnable bgSound = new Runnable() {
#Override
public void run() {
backgroundSound.setLooping(true);
backgroundSound.start();
Log.v(BG_SOUND_CHECK, "After loop Started!");
}
}
Use it with handler.postDelayed(bgSound, 3000)
In your onClick, if you want to stop it from playing before it starts, can use handler.removeCallbacks(bgSound), to stop it after it plays, your pause(). If you don't care to know if it has or hasn't started, you can do both alongside each other, should work fine too.
(if you don't want the music to play after you've left your main activity, you should also do the same actions in your onDestroy and/or onPause)
I want to make a temporary Activity that allowed me to show it , For Example it is appear only for 5 seconds so how can I make it >> Like a hello screen in the beginning in my app .
By the way I am using Android Studio
The splash screen displays for 2 seconds and then the main activity of the application appears. For this, we add a java class that displays the splash screen. It uses a thread towait for 2 seconds and then it uses an intent to start the next activity.
public class SplashScreen extends Activity {
private long ms=0;
private long splashTime=2000;
private boolean splashActive = true;
private boolean paused=false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
Thread mythread = new Thread() {
public void run() {
try {
while (splashActive && ms < splashTime) {
if(!paused)
ms=ms+100;
sleep(100);
}
} catch(Exception e) {}
finally {
Intent intent = new Intent(SplashScreen.this, Splash.class);
startActivity(intent);
}
}
};
mythread.start();
}
}
I have two simple activities MainActivity and ThreadActivity. I call ThreadActivity from MainActivity.
The code ofMainActivity:
public class MainActivity extends Activity {
private Button btn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button)findViewById(R.id.btn2);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, ThreadActivity.class);
startActivity(intent);
}
});
}
}
And the code of ThreadActivity:
public class ThreadActivity extends Activity{
private Thread myThread=null;
Button btn;
int i = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.custom);
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
runThread();
}
});
}
void runThread(){
myThread = new Thread() {
public void run() {
while (i++ < 1000) {
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
btn.setText("#" + i);
Log.d("Thread", "I am running " + i);
}
});
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
}
}
};
myThread.start();
}
}
When I start ThreadActivity I run a simple thread and change button text.
My Problem
When I loose focus from application, i.e when application becomes partially visible, and I come back I am redirected to ThreadActivity and the thread is still running.
When I leave application running and open a new application, and then come back, I am again redirected to ThreadActivity.
The problem is when I press back button, I am being redirected to first activity MainActivity. But instead when back button is being pressed I want my application to exit. In a few words MainActivity should not exist in the stack.
I tried setting android:noHistory="true" for MainActivity but I could not keep the behavior explained in bullet points working. I mean when I pause the application and restore it back, it redirected me to MainActivity instead of ThreadActivity.
Just call finish() when starting the ThreadActivity:
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, ThreadActivity.class);
startActivity(intent);
finish();
}
});
BUT there is a problem with your app. Use a Timer to set the text of the Button! By using a Thread like you do you are creating a memory leak and that is very bad. Try this:
private int i = 0;
private Timer timer;
private final TimerTask timerTask = new TimerTask() {
#Override
public void run() {
btn.post(new Runnable() {
#Override
public void run() {
btn.setText("#" + i++);
}
});
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.custom);
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
timer = new Timer();
timer.schedule(timerTask, 300, 300);
}
});
}
#Override
public void onPause() {
super.onPause();
if(timer != null) {
timer.cancel();
}
}
From your comments to other peoples' answers, it seems like you want the ThreadActivity to always be resumed instead of the MainActivity when your thread is running.
Do the thread in a Service - the service will mean your application's VM is likely to be kept alive longer. An app with no foreground activities can be killed off quite quickly (even if it has background threads running).
You need to persist that the thread is running, and the progress (if, in the real code that is applicable). Currently you could persist the value of i in your while loop.
Your application's default launcher activity (MainActivity) will launch when you click on it from your launcher. Check if the persisted value has been set, and act as though the user started the ThreadActivity in onCreate, if you finish() in onCreate, the user won't see any UI from the MainActivity
Depending on what you're actually trying to do, you might be able to resume the thread depending on the progress persisted - in this example, you could start from the persisted value of i (instead of 0).
What I had to is set android:noHistory="true" for MainActivity and in the ThreadActivity I had to add the solution mention by #NeTeInStEiN in this quesiotn
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
moveTaskToBack(true);
return true;
}
return super.onKeyDown(keyCode, event);
}