I am having trouble with audio in Android. Here is the deal, I have a very simple splash screen. My intention is once the screen loads it will play a very small audio file.
The problem is that a lot of the time, the audio will play before the splash screen actually appears.
Is there a way programmatically to verify that the screen has loaded? I do not want to add an unnecessary timer to make sure the sound doesn't play before it loads. And I want the sound to play at the exact moment the screen appears.
Here is a snippet from my xml file:
<ImageView
android:src="#drawable/splash"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
splash is a .png image
And here is the onCreate code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.scarysplash);
mpPlaySound = MediaPlayer.create(this, R.raw.scream1);
mpPlaySound.start();
}
}
Thanks in advance for your help!
you are not the only only one who has similar problem:)) There are couple of ways how to do that, the popular one is to implement OnGlobalLayoutListener(), e.g.:
View yourView = ...;
yourView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener()
{
// -----------------------------------------
//
// Callback method to be invoked when the
// global layout state or the visibility
// of views within the view tree changes
//
// -----------------------------------------
#Override
public void onGlobalLayout()
{
// Remove view
View yourView = findViewById( R.id.yourId );
if ( yourView != null )
{
yourView .getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
// PLAY SOUND
}
});
Petr
Related
I have a pretty basic task: implementing HLS video streaming using Leanback framework and Exoplayer.
I use a simple single-fragment layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/video_fragment"
android:name="android.support.v17.leanback.app.VideoFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Apparently, VideoFragment comes with a default HUD which looks like this:
According to the docs, the recommended approach to bridging media player with playback controls is via PlaybackGlue. For example, Leanback showcase app uses MediaPlayer and thus uses a MediaPlayerGlue. I, for my purposes, have taken ExoPlayerGlue from this project.
private PlaybackFragmentGlueHost glueHost;
private PlaybackControlGlue glue;
#Override
public void onCreate(Bundle savedInstanceState) {
// ...
final VideoFragment videoFragment = (VideoFragment) getFragmentManager.findFragmentById(R.id.video_fragment);
glueHost = new VideoFragmentGlueHost(videoFragment);
glue = ExoPlayerGlue(this, player)
glue.setHost(glueHost);
}
This code does the job: it binds clicks to player events, so pause/resume and rewinding/fast-forwarding work as expected.
The tricky part is customizing playback controls. PlaybackGlueHost provides setPlaybackRow(Row row) and setPlaybackRowPresenter(PlaybackRowPresenter presenter) methods. Worth pointing out is that these methods have no effect in onCreate() so I either have to use handler or move them to onPostCreate():
#Override
public void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Buttons I need in my HUD
final Action skipPreviousAction = new PlaybackControlsRow.SkipPreviousAction(this); // |<
final Action rewindAction = PlaybackControlsRow.RewindAction(this); // <<
final Action playPauseAction = PlaybackControlsRow.PlayPauseAction(this);
final Action fastForwardAction = PlaybackControlsRow.FastForwardAction(this); // >>
final Action skipNextAction = PlaybackControlsRow.SkipNextAction(this); // >|
final Action repeatAction = PlaybackControlsRow.RepeatAction(this);
final PresenterSelector controlPresenterSelector = ControlButtonPresenterSelector();
final PlaybackControlsRow playbackControlsRow = PlaybackControlsRow();
final ArrayObjectAdapter primaryAdapter = ArrayObjectAdapter(controlPresenterSelector);
final ArrayObjectAdapter secondaryAdapter = ArrayObjectAdapter(controlPresenterSelector);
// First row
primaryAdapter.add(skipPreviousAction);
primaryAdapter.add(rewindAction);
primaryAdapter.add(playPauseAction);
primaryAdapter.add(fastForwardAction);
primaryAdapter.add(skipNextAction);
// Second row
secondaryAdapter.add(repeatAction);
playbackControlsRow.primaryActionsAdapter = primaryAdapter;
playbackControlsRow.secondaryActionsAdapter = secondaryAdapter;
// Applying row to glue host
glueHost.setPlaybackRowPresenter(PlaybackControlsRowPresenter());
glueHost.setPlaybackRow(playbackControlsRow);
}
Now I have my customized playback row but the player no longer responds to button clicks.
I'm having a hard time trying to figure out how PlaybackGlue actually reacts to events from a PlaybackGlueHost. I know I can set OnActionClickedListener in a row presenter and handle actions manually but it kind of renders glue/host interaction model useless. How did it work in the first place? There is no default playback row I could find... I see that each Action corresponds to a set of key codes but I'm not sure what I can make of it.
Could anyone point me in the right direction? There's surprisingly little information on the subject.
Thanks.
Turned out PlaybackControlGlue had all the necessary methods (setControlsRow(), onCreatePrimaryActions(), onCreateSecondaryActions()) which I totally missed in the implementation I borrowed. The glue takes care of providing them to its host in onAttachedToHost() and then sets neccessary listeners.
I have a video url, which I can play with whatever player or VideoView. But here is what I am trying to do.
There is RecyclerView in which each element contains a video's title and thumbnails. When user clicks on the video a popup window appear. This popup window contains a VideoView (or whatever can play a video from url) and plays the video. The user can drag this window around. The trick is to make it so, that if user presses the back button the current activity closes, the window should remain and continue playing the video. While the video is being played the user can do whatever he wants in other apps, while the window with the video remains on top and continues to play the video. At any time user can press on the window (or on button in it) and video will become fullscreen. At the time user decides to play fullscreen the app activity might not be there.
Something like I described above is implemented in this app
https://play.google.com/store/apps/details?id=com.djit.apps.stream&hl=en
I can create a window with a view which will remain there when the user closes the app activity. For this purpose I use this library
https://github.com/pingpongboss/StandOut
If you check the source code of the lib you will see that StandOutWindow is Service which creates a popup window.
I actually can play a video in this window. Here is my window class
public class FloatingWindow extends StandOutWindow {
private static final int WIDTH = 400;
private static final int HEIGHT = 361;
/*
I didn't think of a better way to set video url
therefore I made it static and set it with a static method
I know it is bad, but it works.
I will do it another way later
Now I have other issues
*/
private static String videoUrl = null;
public static void setVideoUrl(String videoUrl) {
FloatingWindow.videoUrl = videoUrl;
}
#Override
public String getAppName() {
return getApplicationContext().getResources().getString(R.string.app_name);
}
#Override
public int getAppIcon() {
return android.R.drawable.ic_menu_close_clear_cancel;
}
#Override
public void createAndAttachView(int id, FrameLayout frame) {
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.floating_window, frame, true);
/*
As you can see I didn't use MediaController for this videoView
VideoView works with it, except for device
On this one device MediaController crashed the app with BadTokenException: token null is not valid, is your activity running
*/
final VideoView videoView = (VideoView) view.findViewById(R.id.textureVideoView);
final ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.videoProgressBar);
final ScrollView description = (ScrollView) view.findViewById(R.id.videoDescription);
videoView.setVideoURI(Uri.parse(videoUrl));
videoView.requestFocus();
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
videoView.start();
/*
I don't want my video view to be visible until it is ready
Because while it is preparing it gets transparent
But I want a progress bar instead
Therefore initially set its dimensions to 1x1 and change the size when it's ready
*/
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
params.gravity = Gravity.CENTER;
videoView.setLayoutParams(params);
progressBar.setVisibility(View.GONE);
}
});
/*
When the activity is not present VideoView crashes when clicked
To avoid crash I overrided setOnTouchListener method and return true from it
*/
videoView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP) {
if (videoView.isPlaying()) {
videoView.pause();
} else {
videoView.start();
}
}
return true;
}
});
}
#Override
public StandOutLayoutParams getParams(int id, Window window) {
return new StandOutLayoutParams(id, WIDTH, HEIGHT,
StandOutLayoutParams.CENTER, StandOutLayoutParams.CENTER);
}
#Override
public int getFlags(int id) {
return super.getFlags(id)
| StandOutFlags.FLAG_BODY_MOVE_ENABLE
| StandOutFlags.FLAG_WINDOW_FOCUSABLE_DISABLE
| StandOutFlags.FLAG_DECORATION_SYSTEM
| StandOutFlags.FLAG_ADD_FUNCTIONALITY_DROP_DOWN_DISABLE
| StandOutFlags.FLAG_DECORATION_CLOSE_DISABLE;
}
#Override
public String getPersistentNotificationMessage(int id) {
return "Click here to close floating window";
}
#Override
public Intent getPersistentNotificationIntent(int id) {
return StandOutWindow.getCloseIntent(this, FloatingWindow.class, id);
}
}
The code above works, but there are several problems with it.
I tested it on three devices and emulator and it worked. But there is a device at which it crashed at first. I remove MediaController and now it almost works. When user changes the size of the window the VideoView size gets weird. Perhaps I should test the device, on which the code does not work for Android compatibility. But, anyway maybe how I am doing the windowed player is wrong and you know a better way.
I have to set the size of the VideoView to 1x1 and then change it, I cannot make it normal size right away, because if I do it the area in the window where VideoView should be becomes transparent until the video is loaded. Also I cannot click on the VideoView and let the system process the click event. If I do it I get BadTokenException: token null is not valid, is your activity running. The activity is not running, therefore I have to process the click event myself to prevent the crash. These hacks make me think that I am doing it wrong.
I cannot think of a way to make video fullscreen. The only thing I thought of is to start a fullscreen activity and pass url and video position to it. Maybe this is the way to go. In this case the user will have to wait for video to load again when (s)he hits the fullscreen button. Plus I have no idea how the window will behave when fullscreen activity is started.
It feels like video was not intended to be played without activity. Maybe there is way to make this window player in such a way that there is always an invisible activity which would miss all the touch events and pass them down to the activity stack. And such an activity could be used for video playback.
You can make it to full screen by inflating the layout with these dimensions:
LayoutParams.y = getApplicationContext().getResources().getDisplayMetrics().heightPixels
LayoutParams.x = getApplicationContext().getResources().getDisplayMetrics().widthPixels
Hi i have two textViews that i initially set its visibility to gone then animate in and become visible. now i want to make the invisible again but for some reason they're still showing on screen does anyone no why?
in my onCreate() i make the view gone
register = (TextView)findViewById(R.id.register);
register.setVisibility(View.GONE);
forgotpassword = (TextView)findViewById(R.id.forgotpw);
forgotpassword.setVisibility(View.GONE);
then later on i make it visible
public void run()
{
animations();
loginForm.setVisibility(View.VISIBLE);
register.setVisibility(View.VISIBLE);
forgotpassword.setVisibility(View.VISIBLE);
}
and then when a user presses a button i want the text views to become invisible so that they retain their layout but they stay visible on screen
signInBtn = (Button) findViewById(R.id.signin);
signInBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
signInProcess();
}
});
public void signInProcess() {
register.setVisibility(View.INVISIBLE);
forgotpassword.setVisibility(View.INVISIBLE);
setuploader.setVisibility(View.VISIBLE);
}
In Android when you animate something, It's just drawn somewhere else. The actual element is not moved. So when you animate signInBtn it's drawn somewhere else, but the actual button is not moved from the original position. So when you click the button the click handler is not called.
To avoid this set fillAfter = True in your animation so the button will actually get moved at the end of your animation.
Also, after animating a view in Android make sure you call View.clearAnimation() before trying to change its visibility.
I have a longClick method on a grid view that does not work for the the 0 index position of the underlying ArrayList. However, if I add only one 'SentenceButton' I can remove it but not if I add many 'SentenceButton's.
The primary code is as follows in the SentenceButton class:
public boolean onLongClick(View view) {
boolean result = false;
SentenceButton btnSentence = (SentenceButton)view;
ArrayList<SentenceButton> temp = Utility.getSentenceArray();
if(temp.contains((SentenceButton)view));
result = Utility.getSentenceArray().remove(btnSentence);
Utility.getSentenceArray().trimToSize();
SBActivity.getmSentenceAdapter().notifyDataSetChanged();
return result;
}
This part of the code is made to simply remove an errant entry.
This does not seem to work, ie. remove, the first element of Utility.getSentenceArray and only works intermittently on the other 'SentenceButtons'.
Strangely, the play sound and adding to the Sentence view from the fragment I am using as a content view only seems to work twice in a row and then seems to hang until I rotate the device. The relevant code follows in the SoundButton class:
public void onClick(View view) {
// To Do: add to current sentence
SoundThread sndThread = new SoundThread();
sndThread.start();
Utility.getSentenceArray().add(new SentenceButton(mContext, (SoundButton)view));
SBActivity.getmSentenceAdapter().notifyDataSetChanged();
}
This adds the content view 'SoundButton' as a 'SentenceButton' in the SentenceButton array. Even more strangely to me, if I rotate the device enough it seems to add the 'SoundButton' as a 'SentenceButton'. It even plays the 'SoundButton' as the rotation happens but not before. It as if the onClick of the SoundButton is queued somewhere.
The rotation code is as follows:
#Override
public void onConfigurationChanged(Configuration newConfig)
{
if (getResources().getConfiguration().orientation ==
Configuration.ORIENTATION_LANDSCAPE)
{
setFragment(getmFrag());
mSentenceGridView.invalidate();
}
else if (getResources().getConfiguration().orientation ==
Configuration.ORIENTATION_PORTRAIT)
{
setFragment(getmFrag());
mSentenceGridView.invalidate();
}
super.onConfigurationChanged(newConfig);
}
Could someone help me with this. Definitely a noob here and of course will add any relevant info asked of me.
I am trying to write a soundboard to help my friend's relative communicate better.
Many thanks in advance and especially to a tutorial on a working soundboard.
I am writing this with a Kindle Fire as my testing device.
I discovered part of the problem was not having a proper id for the buttons I am creating
Cant figure this out (am a noob) but I have a default activity that starts with just one image button, clicking that image button opens the second activity that contains a bunch of image buttons but only one has an onclick, this is the exact code:
<ImageButton android:src="#drawable/level1"
android:layout_width="wrap_content" android:id="#+id/imageButton1"
android:layout_height="wrap_content"
android:onClick="button_clicked1">
</ImageButton>
in the java file this is my code:
public void button_clicked1(View v) {
//text1.setText("clicked");
//ib2.setImageResource(R.drawable.level5);
}
The screwy part, if that above function is empty nothing happens but no matter what I put in the above function... for example even a simple settext like the above commented one, and it force closes on me :((
the full java file if you are interested: http://pastebin.com/W4sJUKXH
and the manifest file (as the problem might be there, like I said, am a noobie) http://pastebin.com/yEuG1su7
** Where i think the error is:
In the manifest file I only have declared the second activity, nothing else...
Set id for the ImageViews, this is how you handle many Buttons with one method
public void button_clicked1(View v) {
switch(v.getId())
{
case R.id.myfirstimage:
//do something
break;
case R.id.mysecondimage:
// do something
break;
// add more cases
default:
// do something if none of the cases is your image view or do nothing
}
}
Set setContentView( ) before you do anything with the UI components. (Thats why you get some Exception)
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.start_levels_screen);
text1 = (TextView) findViewById(R.id.textView1);
ib2 = (ImageButton) findViewById(R.id.imageButton2);
}