I am new to android, I have a MaterialButton and I would like its icon to change, but the activity suddenly stops, I do not see where the problem comes from, I show you the code, it is an audio player. (if you need all code of class I send u, inform me)
xml code :
<com.google.android.material.button.MaterialButton
android:id="#+id/btn_play_music"
style="#style/myTheme.btn_last_music"
android:layout_marginLeft="#dimen/dim_10"
android:layout_marginRight="#dimen/dim_10"
app:icon="#drawable/ic_baseline_play_arrow_24"
tools:ignore="SpeakableTextPresentCheck"
/>
onCreate :
btnPlay = (MaterialButton) findViewById(R.id.btn_play_music);
btnPlay.setOnClickListener(this);
btnPlay.setTag(5);
method to play music :
public void playIfItemClicked(SearchMusic lm, int i) {
mediaPlayer.reset();
Uri u = lm.getListMusic().get(i).getUri();
InteractiveMusic intMus = new InteractiveMusic(mediaPlayer, u);
try {
intMus.playMusic();
btnPlay.setIconTintResource(R.drawable.ic_pause);
} catch (IOException e) {
e.printStackTrace();
}
}
ok, i have find the solution,
it's this line :
btnPlay.setIconTintResource(R.drawable.ic_pause);
change to :
btnPlay.setIconResource(R.drawable.ic_pause);
Related
I'm trying to play a music file, and then a second file which is a second part of the song. The problem is, the transition between the two is very lacking. After one part finishes playing, there's about a second of silence, and then the second part starts playing.
In best case, both files would play seamlessly one after another. I tried using a second MediaPlayer, preparing it beforehand so the only thing left is to start it, but it didn't help. Is there a way to start playing the second part earlier?
Here's the code:
// MediaPlayers are created as "music" and "musicfollow"
//setting up files for source
AssetFileDescriptor anticip = getAssets().openFd("file1.ogg");
AssetFileDescriptor afdass1 = getAssets().openFd("file2.ogg");
//setting a new source for player
music.reset();
try {
music.setDataSource(anticip.getFileDescriptor(), anticip.getStartOffset(),anticip.getLength());
music.prepare();
} catch (IOException e) {
e.printStackTrace();
}
//preparing follow-up music player
musicfollow.reset();
musicfollow.setLooping(true);
try {
musicfollow.setDataSource(afdass1.getFileDescriptor(), afdass1.getStartOffset(),afdass1.getLength());
musicfollow.prepare();
} catch (IOException e) {
e.printStackTrace();
}
//playing part 1
music.start();
//changing the MediaPlayer to follow-up player
music.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
musicfollow.start();
}
});
}
I added a feature based on the Codelabs tutorial from Google (https://codelabs.developers.google.com/codelabs/sceneform-intro/index.html?index=..%2F..index#15) which allows users to take photos of AR objects that were added into the scene. The code works fine, however, I wish to hide the PlaneRenderer (the white dots that appear when ARCore detects a surface) in the photo taken by users.
In the onClickListener for the "Capture Photo" button, I tried setting PlaneRenderer to invisible before the takePhoto() is called. This hid the PlaneRenderer on screen, but not in the photo captured.
This is my onClickListener:
capturePhotoBtn.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
arFragment.getArSceneView().getPlaneRenderer().setVisible(false);
for (TransformableNode vNode : videoNodeList){
if (vNode.isSelected()){
vNode.getTransformationSystem().selectNode(null);
}
}
takePhoto();
}
});
videoNodeList contains a list of transformableNodes, and is used to keep track of the objects added by users (as users can add more than 1 object into the scene). As the objects are transformableNodes, users can tap on them to resize/rotate, which shows a small circle underneath the selected object. So, the for-loop added is to de-select all transformableNodes when taking photos, to ensure that the small circle does not appear in the photo as well.
The takePhoto() method is from the CodeLabs tutorial, and is given as follows:
private void takePhoto() {
final String filename = generateFilename();
ArSceneView view = arFragment.getArSceneView();
// Create a bitmap the size of the scene view.
final Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
Bitmap.Config.ARGB_8888);
// Create a handler thread to offload the processing of the image.
final HandlerThread handlerThread = new HandlerThread("PixelCopier");
handlerThread.start();
// Make the request to copy.
PixelCopy.request(view, bitmap, (copyResult) -> {
if (copyResult == PixelCopy.SUCCESS) {
try {
File file = saveBitmapToDisk(bitmap, filename);
MediaScannerConnection.scanFile(this,
new String[] { file.toString() }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
}
});
} catch (IOException e) {
Toast toast = Toast.makeText(ChromaKeyVideoActivity.this, e.toString(),
Toast.LENGTH_LONG);
toast.show();
return;
}
Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content),
"Photo saved", Snackbar.LENGTH_LONG);
snackbar.setAction("Open in Photos", v -> {
File photoFile = new File(filename);
Uri photoURI = FileProvider.getUriForFile(ChromaKeyVideoActivity.this,
ChromaKeyVideoActivity.this.getPackageName() + ".provider",
photoFile);
Intent intent = new Intent(Intent.ACTION_VIEW, photoURI);
intent.setDataAndType(photoURI, "image/*");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
});
snackbar.show();
} else {
Toast toast = Toast.makeText(ChromaKeyVideoActivity.this,
"Failed to copyPixels: " + copyResult, Toast.LENGTH_LONG);
toast.show();
}
handlerThread.quitSafely();
}, new Handler(handlerThread.getLooper()));
}
To give you a clearer picture, PlaneRenderer is hidden on the device screen when the "Capture Photo" button is tapped. This is what is seen immediately after the user taps on the "Capture Photo" button:
However, PlaneRenderer still appears in the photo taken. This is the resulting image that was taken:
which is not what I was looking for as I want to hide the PlaneRenderer in the photo (ie. photo taken should not have the white dots)
Users of this app add objects by selecting an object from the menu and tapping on the PlaneRenderer, so disabling the PlaneRenderer totally is not feasible. In addition, I have another video recording feature in the app that managed to successfully hide the PlaneRenderer in the recording by simply setting PlaneRenderer to invisible, so I am not sure why it doesn't work when capturing photos.
Any help will be greatly appreciated! :)
Finally figured this out after countless of hours. Sharing my solution (may not be the best solution) in case anyone faces this same issue in the future.
I discovered that due to the handlerThread used, the takePhoto() method always happens before the PlaneRenderer was set to invisible whenever the button is tapped. So, I added a short delay to ensure that the reverse happens - ie. delay takePhoto() method for a short while such that the method will always happen after the planeRenderer is invisible.
Here is the code snippet:
capturePhotoBtn.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
arFragment.getArSceneView().getPlaneRenderer().setVisible(false);
for (TransformableNode vNode : videoNodeList){
if (vNode.isSelected()){
vNode.getTransformationSystem().selectNode(null);
}
}
v.postDelayed(new Runnable() {
public void run() {
takePhoto();
}
}, 80);
}
});
This method worked for me, but I am sure there are better solutions to solve this problem. Hope this helps someone with the same problem and feel free to contribute if you know of a better solution.
Assume I have the AOSP source code, How can I pause the APP in the foreground when pulling down the notification panel? I've googled and find an APP can listen the event onWindowFocusChange and take some action proactively, but how can I pause ANY APP when pulling down the notification panel, without modify every APP respectively (which is impractical)?
Is there a way that I can call the onPause function of any foreground App from the SystemUI process?
You can use below method for detecting the notification panel pull,
In your manifest file
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
In your activity override the onWindowFocusChanged() and write the below code.
This uses the permission
#Override
public void onWindowFocusChanged(boolean hasFocus)
{
try
{
if(!hasFocus)
{
Object service = getSystemService("statusbar");
Class<?> statusbarManager = Class.forName("android.app.StatusBarManager");
Method collapse = statusbarManager.getMethod("collapse");
collapse .setAccessible(true);
collapse .invoke(service);
}
}
catch(Exception ex)
{
if(!hasFocus)
{
try {
Object service = getSystemService("statusbar");
Class<?> statusbarManager = Class.forName("android.app.StatusBarManager");
Method collapse = statusbarManager.getMethod("collapse");
collapse .setAccessible(true);
collapse .invoke(service);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ex.printStackTrace();
}
}
}
Then in your app request for audio focus, refer below link
https://developer.android.com/guide/topics/media-apps/audio-focus#java
This will pause audio access for all other apps.
There are two parts to the solution
detecting notification panel pull
pausing any media playing app
For detecting notification panel pull you may employ the method suggested by Akash or same code here by Lalith.
In your manifest file
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
In your activity override the onWindowFocusChanged() and write the below code.
#Override
public void onWindowFocusChanged(boolean hasFocus)
{
try
{
if(!hasFocus)
{
Object service = getSystemService("statusbar");
Class<?> statusbarManager = Class.forName("android.app.StatusBarManager");
Method collapse = statusbarManager.getMethod("collapse");
collapse .setAccessible(true);
collapse .invoke(service);
}
}
catch(Exception ex)
{
if(!hasFocus)
{
try {
Object service = getSystemService("statusbar");
Class<?> statusbarManager = Class.forName("android.app.StatusBarManager");
Method collapse = statusbarManager.getMethod("collapse");
collapse .setAccessible(true);
collapse .invoke(service);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ex.printStackTrace();
}
}
}
For the second part all well designed media players implement Audio Focus Change Listeners. Thus to pause all media player apps you can request temporary or full audio focus depending on whether you want to let the apps play again as soon as notification panel is collapsed.
Use following
AudioManager audioManager = (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE);
playbackAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
// replace AUDIOFOCUS_GAIN with AUDIOFOCUS_GAIN_TRANSIENT if you want to pause and again resume when notification panel collapsed
.setAudioAttributes(playbackAttributes)
.setAcceptsDelayedFocusGain(true)
.setOnAudioFocusChangeListener(afChangeListener, handler)
.build();
// to requestAudioFocus and pause other media apps
audioManager.requestAudioFocus(focusRequest);
// to resume again in case you requested AUDIOFOCUS_GAIN_TRANSIENT
audioManager.abandonAudioFocus(afChangeListener);
Don't forget to implement AudioFocusChangeListener
AudioManager.OnAudioFocusChangeListener afChangeListener = new AudioManager.OnAudioFocusChangeListener() {
#Override
public void onAudioFocusChange(int focusChange) {
// leave this empty if you don't need to handle audio focus for your app
}
};
Is there a way that I can call the onPause function of any foreground App from the SystemUI process?
As far as I have researched system ui process is not accessible. However, you can launch an activity with transparent theme.
In your manifest
<activity android:name=".your.activity" android:theme="#android:style/Theme.Translucent.NoTitleBar" />
see this and I have also personally tested creating transparent activity.
This means that the activity on top now is your transparent activity and the onPause method of the other activity will naturally be called. Once the notification panel is collapsed you can destroy this activity. And to be on safe side you can also set onTouch listener by creating an empty view that feels the screen which should destroy the activity onTouch.
Assuming that the Android device use the default MediaPlayer, according to this state diagram, you may use the native pause and stop state.
If you only wan't to stop sound, you may probably change the sound volume using a VolumeAutomation ?
I'm starting to learn how to build an Android TV for the first time so I apologize for a likely obvious answer to this question. Basically, I'm trying to replicate how the Android TV Youtube App displays when there is no internet connectivity as shown:
YouTube shows the main fragment (I believe) in the back with no videos, and there seems to be a transparent overlay with an ImageView, TextView, and Button. Once there is internet connectivity and the user taps the Retry button, it loads the video content and the overlay disappears.
I'm trying to achieve this exact same technique, and with internet connectivity, I currently have this:
With no internet connectivity, I'd like to show something similar to how YouTube does.
Here's some of my basic code:
MainActivity.java:
public class MainActivity extends Activity
{
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main_browse_fragment"
android:name="com.ui.MainFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.ui.MainActivity"
tools:deviceIds="tv"
tools:ignore="MergeRootFrame" />
MainFragment.java:
public class MainFragment extends DetailsFragment
{
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
setupUIElements();
setupThings();
}
public void setupThings()
{
if (isOnline() == true)
{
prepareBackgroundManager();
loadRows();
setupEventListeners();
}
else
{
// I don't know what to do here???
}
}
public Boolean isOnline()
{
try
{
Process p1 = java.lang.Runtime.getRuntime().exec("ping -c 1 www.google.com");
int returnVal = p1.waitFor();
boolean reachable = (returnVal == 0);
return reachable;
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
}
Simple, if there's internet connection, I load the videos from online, otherwise, I inform the user like YouTube does.
Where I'm stuck is in the else statement. I'm not sure exactly what to create there to achieve this overlay.
I've looked at some solutions regarding switching fragments here:
Switching between Fragment view
How to switch between fragments during onclick?
However, I'm not sure if, for my purpose, I would need to do this?
Can someone point me in the right direction? I'm not looking to have someone write the whole code exactly like the YouTube app, but I'd like to have a small sample, with just maybe a Button or TextView? This will allow me to follow the structure and modify it to meet my requirements.
Thanks.
You can look at the Leanback Sample app, which contains an example of a BrowseErrorFragment.
To display:
BrowseErrorFragment errorFragment = new BrowseErrorFragment();
getFragmentManager().beginTransaction().replace(R.id.main_frame, errorFragment)
.addToBackStack(null).commit();
I'm making a simple splash screen so that when an app loads it shows a small logo and plays a little jingle.
I've set it up as so:
splashSong = MediaPlayer.create(MainActivity.this, R.raw.splash);
splashSong.start();
Thread splashThread = new Thread(){
public void run(){
try{
sleep(6000);
}
}catch (InterruptedException e){
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
Intent openMenu = new Intent("com.mmm.MAINMENU");
startActivity(openMenu);
}
}
};
splashThread.start();
However there is an issue with the play count. When it loads the splash class, the jingle is played twice. I've changed a series of things such as preparing the song and setting the datasource. This is, however, not successful and the screen still plays the song twice.
Anybody have any ideas as to why it might be doing this?
Thanks,
Add
splashSong.setLooping(false);
Thanks to Mr. Me for the help. It was to do with the applications orientation. Removed that and all worked!