MediaRouteButton is not active in Fragment - android

I use button for starting chromecast android.support.v7.app.MediaRouteButton in my app in activities xml and fragments xml with videoplayer.
For initializing cast button I use the next code:
private void setupChromeCast() {
try {
CastButtonFactory.setUpMediaRouteButton(getActivity(), castButton);
castContext = CastContext.getSharedInstance(getActivity());
castSession = castContext.getSessionManager().getCurrentCastSession();
onCastStateChanged(castContext.getCastState());
castSessionManager = new CastSessionManager(this);
isChromeCastAvailable = true;
} catch (Exception e) {
isChromeCastAvailable = false;
}
}
And it works fine in activities. I mean, when chromecast device is near, my MediaRouteButton becomes active and I can press it. But when this Button is on Fragment, it does not become active. And callback
#Override
public void onCastStateChanged(int state)
doesnt call. So, how to fix this bug? And there is one interesting moment: when I`m in fragment, button is not active, but when I hide my app into background, and then open into foreground, my mediaroutebutton becomes active. Its so strange.

In your activity, initialize Cast:
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CastContext.getSharedInstance(this);
}
Then in your fragment you can get hold of the Cast context:
#Override
public void onResume() {
super.onResume();
CastContext cc = CastContext.getSharedInstance();
cc.addCastStateListener(this);
cc.getSessionManager().addSessionManagerListener(
this, CastSession.class);
}
Verified with Cast version 18.1.0.

Taken from example docs:
"In order to integrate Google Cast functionality, the Activities need to inherit from either the AppCompatActivity or its parent the FragmentActivity. This limitation exists since we would need to add the MediaRouteButton (provided in the MediaRouter support library) as an MediaRouteActionProvider and this will only work if the activity is inheriting from the above-mentioned classes."
Are you sure you have inherited FragmentActivity or AppCompatActivity?
If you ever got it to work let me know, I want it to work this way too.

Related

Android - App crashes at re-launch after app being force closed by OS because of permission disabled

Steps to produce the problem.
1) Launch Tasks.class activity
2) Hit home button puting the app in the background
3) Go to Settings->permissions and disable a permission which results in my app being force closed by the OS
4) Open app from launcher ---> app resumes Tasks.class activity instead of opening MainActivity --> CRASH
It crashed at listView.setAdapter(mAdapter); because listView was now null as were all class variables; Here is my activity. It uses the TasksFragment class as data retainer to handle orientation changes. When the TasksFragment is created it starts loading data with an AsyncTask and notifies the Activity when new data are fetched using the callbacks
public class Tasks extends FragmentActivity implements TasksFragment.TaskCallbacks{
TasksFragment f;
FragmentManager fm;
String TASKS_TAG="TASKS";
searchList = new ArrayList<Task>();
TaskAdapter mAdapter;
ListView listView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tasks);
.
.
.
fm = getSupportFragmentManager();
f = (TasksFragment) fm.findFragmentByTag(TASKS_TAG);
if (f == null) {
f = new TasksFragment();
fm.beginTransaction().add(f, TASKS_TAG).commit();
}
else{
preLoad();
postLoad();
}
}
#Override //TaskFragmentCallback
public void onPreExecute() {
preLoad();
}
#Override //TaskFragmentCallback
public void onProgressUpdate(Task task) {
searchList.add(task);
mAdapter.notifyDataSetChanged();
}
#Override //TaskFragmentCallback
public void onCancelled() { }
#Override //TaskFragmentCallback
public void onPostExecute() {
postLoad();
}
public void preLoad(){
searchList = new ArrayList<Task>();
mAdapter = new TaskAdapter(this,searchList,true);
listView.setAdapter(mAdapter);
}
public void postLoad(){
searchList.clear();
searchList.addAll(f.taskList);
mAdapter.notifyDataSetChanged();
}
}
I managed to make it not crash replacing the onPreExecute callback with
#Override //TaskFragmentCallback
public void onPreExecute() {
if(listView!=null){
preLoad();
}
}
I used log statements to determine the sequence of events and I saw the following:
First app launch and lauch Tasks.class
ON ACTIVITY CREATE
ON FRAGMENT CREATE
on pre execute callback
preload called by callback
ON ACTIVITY RESUME
On reopen after app termination while Tasks.class was open and app was put to background
ON FRAGMENT CREATE
on pre execute callback //this is where it would crash
ON ACTIVITY CREATE
preload called by activity because fragment!=null
ON ACTIVITY RESUME
I can't understand why the fragment is created before the activity...who called this fragment to be created? Though the if(listView!=null) condition solved my problem I think it's a bandaid and not a real solution. Can someone shed some wisdom on this problem ?
What I believe is happening (somebody correct me if I'm wrong) is that because you're artificially shutting down the application with System.exit(0) which is not a recommended practice (so far as I know) in Android, your Fragment isn't being cleanly detached. This results in the Fragment being resumed when you open the app back up.
The check you're doing if (listView != null) works because your Fragment is sort of a ghost. It still exists, but it's not attached to the activity and so it can't obtain a reference to the ListView you're trying to use. A similar, but more "valid" check might be if (this.getActivity() != null).
From android System documentation
Causes the VM to stop running and the program to exit with the given exit status. If runFinalizersOnExit(boolean) has been previously invoked with a true argument, then all objects will be properly garbage-collected and finalized first.
Not sure it it will make a difference. Generally I never exit() an android program.

Android : Use fragment or change all the view in the activity

I ask you a suggestion for my application. I have to develop one app with this characteristics :
1- only portrait
2- when open the app it shows the bluetooth devices presents
3- when you click on one device the app ask to the user an unlock code and show the connect button
4- after press connect button the app show a loading spinner bar whith two o three buttons
The point is : is It better that I used three different fragment for each behavior or not ?
For now I have did :
one activity for the scan device
one activity for the unlock code
but i don't know where I can put the loading screen (loading spinner bar and the three buttons)
Now I'm thinking of develop in different way. One central activity that handles the loading of 3 different fragments :
one for scan device
one for unlock code and one for loading screen
But I'm new in the Android programming, and I always wonder if I think in correct way or in the wrong way .
And in last : for communicate the chosen device from fragment to the activity I think I will implement a listener in mainactivity. Is it right ?
****EDIT :** *I have another doubt regarding the main question.***
Now after your advice I want develop this app in this way :
Main Activity
Scan DEvice fragment
Unlock Device fragment
Loading fragment
Started Under Service
In the precedent version I thought to develop three different activity and to use binder and messange to communicate in two direction way to/from the service
Now,instead, there are three different fragments inside the main activity. My question is : for you is better implement the comunication to the service inside mainactivity or inside every single fragment ?
For instance : User selects a device in the scan fragment, this fragment communicates the choise directly to the service or communicates the choise to the mainactivity which forwards the information to the service ?
Thanks for your time :)
Do not use setContentView() to change between states of the application, it can cause inconsistency problems on onBackPressed() - use fragments instead. But you will run into a problem on back press, so you will need to see if there are fragments left in your activity on back press, for which you will need to see https://stackoverflow.com/a/24527530/2413303 ContainerFragment in this question.
#Override
public void onCreate(Bundle saveInstanceState)
{
super.onCreate(saveInstanceState);
this.setContentView(R.layout.activity_container);
if (saveInstanceState == null)
{
getSupportFragmentManager().beginTransaction()
.add(R.id.activity_container_container, new ExampleFragment())
.addToBackStack(null)
.commit();
}
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener()
{
public void onBackStackChanged()
{
int backCount = getSupportFragmentManager().getBackStackEntryCount();
if (backCount == 0)
{
finish();
}
}
});
}
Also, use ButterKnife library, I didn't in this example I linked, even though I should have. It makes the code much less verbose and do the same thing.
public class FancyFragment extends Fragment {
#InjectView(R.id.button1) Button button1;
#InjectView(R.id.button2) Button button2;
#OnClick(R.id.btnSubmit)
public void submit(View view) {
// TODO submit data to server...
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fancy_fragment, container, false);
ButterKnife.inject(this, view);
// TODO Use "injected" views...
return view;
}
}
And for communication, you can use Otto library as per https://stackoverflow.com/a/28480952/2413303
public class UpdateListEvent {
}
public class MainActivity extends ActionBarActivity {
...
public void updatelist() {
SingletonBus.INSTANCE.getBus().post(new UpdateListEvent());
}
}
public class FragmentA extends Fragment {
#Override
public void onResume() {
super.onResume();
SingletonBus.INSTANCE.getBus().register(this);
}
#Override
public void onPause() {
SingletonBus.INSTANCE.getBus().unregister(this);
super.onPause();
}
#Subscribe
public void onUpdateListEvent(UpdateListEvent e) {
//do things
}
}
public enum SingletonBus {
INSTANCE;
private Bus bus;
private SingletonBus() {
this.bus = new Bus(ThreadEnforcer.ANY);
}
public Bus getBus() {
return bus;
}
}
I have another doubt regarding the main question.
Now I develop this app in this way :
Main Activity
Scan DEvice fragment
Unlock Device fragment
Loading fragment
Started Under Service
In the precedent version I thought to develop three different activity and to use binder and messange to communicate in two direction way to/from the service
Now,instead, there are three different fragments inside the main activity, for you is better implement the comunication to the service inside mainactivity or inside every single fragment ?
For instance :
User selects a device in the scan fragment, this fragment communicates the choise directly to the service or communicates the choise to the mainactivity which forwards the information to the service ?

Callback When DialogFragment is Dismissed

I want to launch a dialog with a custom layout, which I've implemented via a DialogFragment. (I basically just changed onCreateView() and added button handlers). The dialog lets the user quickly change an important setting.
This dialog will be launched from several different activities. The different activities don't have much in common, except that they need to refresh after the user makes a change to the setting. They don't need to get any information from the dialog; they merely need to know when it's closed (dismissed).
What I've Tried
I tried having the activity refresh in onResume(), but launching and dismissing a dialog never seems to call this method. (So I'm not sure why it even exists, but that's probably a topic for another question.)
Next, I tried adding a DialogInterface.OnDismissListener to the dialog:
public static void showMyDialog(OnDismissListener listener, Activity activity)
{
DialogFragment fragment = new MyDialogFragment();
fragment.show(activity.getFragmentManager(), "date");
activity.getFragmentManager().executePendingTransactions();//A
fragment.getDialog().setOnDismissListener(listener);//B
}
When I originally left out the line A, I got a NullPointerException on line B because the dialog is null at that point. Following the advice of this SO answer, I put in the call to executePendingTransaction(). This causes an IllegalStateException on line B, with the message "OnDismissListener is already taken by DialogFragment and cannot be replaced." I also tried putting setOnDismissListener() before the call to show(), but that always caused a NullPointerException.
I then read this other SO answer, which says the original asker was "calling getDialog() too early in the DialogFragment's life cycle." So I tried adding a constructor to my DialogFragment:
public MyDialogFragment(SomeCallback illTakeAnythingICanGet)
{
//I'll store the callback here and call it later
}
Unfortunately, adding a constructor made Android Lint freak out with a fatal warning, and when I looked it up, I found a comment in this question that seems to say this approach will make it impossible to deal with the user rotating the screen while the dialog is open.
The Question
How can an activity figure out when a DialogFragment has closed (been dismissed) in a way that won't break my app if the user rotates the screen? Should I be using something else besides a DialogFragment?
This is just a longer explanation of harism's comment in case anyone else has the same problem I did.
You can accomplish what I wanted by creating an interface like this:
public interface MyDialogCloseListener
{
public void handleDialogClose(DialogInterface dialog);//or whatever args you want
}
Have the activity that launches your dialog (DialogFragment) implement this interface. Then give that DialogFragment the following method:
public void onDismiss(DialogInterface dialog)
{
Activity activity = getActivity();
if(activity instanceof MyDialogCloseListener)
((MyDialogCloseListener)activity).handleDialogClose(dialog);
}
More explanatory code for someone to do the same.
Create the interface as:
package com.example.dialoglistener;
import android.content.DialogInterface;
public interface MyDialogCloseListener {
public void handleDialogClose(DialogInterface dialog);
}
Implement the interface in activity as:
MyDialogCloseListener closeListener = new MyDialogCloseListener() {
#Override
public void handleDialogClose(DialogInterface dialog) {
//do here whatever you want to do on Dialog dismiss
}
};
Write a DismissListener in DialogFragement as
public void DismissListener(MyDialogCloseListener closeListener) {
this.closeListener = closeListener;
}
call DismissListener from your activity as:
dialogFragementObject.DismissListener(closeListener);
and finally write onDismiss method
#Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
if(closeListener != null) {
closeListener.handleDialogClose(null);
}
}
Tyler's example was the only example I could find that actually worked. The only thing that needs changed for the example to work is the call to the DismissListner method in the DialogFragment class. He has it as:
dialogFragementObject.DismissListner(closeListener);
This just needs to be a cast to whatever your class name of that DialogFragment is. For example:
((MyDialogFragment)dialogFragementObject).DismissListner(closeListener);

Flurry context in a Fragment

I need to use Flurry within a Fragment in my Android app.
I insert the following code in onStart():
#Override
public void onStart() {
super.onStart();
//Log.i("About get activity","About get activity "+getActivity().hashCode());
FlurryAgent.onStartSession(getActivity(), "WXXXXXXXX");
}
and in on stop:
#Override
public void onStop() {
FlurryAgent.onEndSession(getActivity());
super.onStop();
}
Is this code correct? Do I pass the context as getActivity(), this or something else?
That's correct, you could also use:
getActivity().getApplicationContext();
which is the context for the entire application and not specific to that particular Activity.
As a side note, if it happens to you to get some weird crashes, specially when you press quickly the back button removing all the fragment in your back stack, it may be that getActivity() is returning null.

Handling onNewIntent in Fragment

I am writing an application that uses NFC to read some data stored on it. My application uses Fragments and Fragment don't come with onNewIntent() method. Since, the data I am reading is done with my separate class which handles NFC related operation, the only thing I need to do is update the TextView inside the Fragment. However this implementation can also be used to pass new Intent to the Fragment.
Here is my current implementation which makes use of an interface. I am calling the listener after new Intent is received and NFC related checks succeeds. This is the FragmentActivity which hosts Fragment.
public class Main extends FragmentActivity implements
ActionBar.OnNavigationListener {
private Bundle myBalanceBundle;
private NFC nfcObj;
private NewBalanceListener newBlanceListener;
#Override
public void onNewIntent(Intent intent) {
setIntent(intent);
}
#Override
protected void onResume() {
getNFCState();
super.onResume();
}
private void getNFCState() {
//Other NFC related codes
else if (nfc_state == NFC.NFC_STATE_ENABLED){
readNFCTag();
}
}
private void readNFCTag() {
//Other NFC related codes
if (getIntent().getAction().equals(NfcAdapter.ACTION_TECH_DISCOVERED)) {
nfcObj.setTag((Tag) getIntent().getParcelableExtra(
NfcAdapter.EXTRA_TAG));
nfcObj.readQuickBalance();
transitQuickReadFragment(nfcObj.getCurrentBalance());
}
}
private void transitQuickReadFragment(String balance) {
// Creates a balance bundle and calls to select MyBalance Fragment if it
// is not visible. Calls listener is it is already visible.
if (actionBar.getSelectedNavigationIndex() != 1) {
if (myBalanceBundle == null)
myBalanceBundle = new Bundle();
myBalanceBundle.putString(Keys.BALANCE.toString(), balance);
actionBar.setSelectedNavigationItem(1);
} else {
newBlanceListener.onNewBalanceRead(balance);
}
}
#Override
public boolean onNavigationItemSelected(int position, long id) {
// Other fragment related codes
fragment = new MyBalance();
fragment.setArguments(myBalanceBundle);
newBlanceListener = (NewBalanceListener) fragment;
// Other fragment related codes
}
// Interface callbacks. You can pass new Intent here if your application
// requires it.
public interface NewBalanceListener {
public void onNewBalanceRead(String newBalance);
}
}
This is MyBalance Fragment which has TextView that needs to be updated whenever NFC is read:
public class MyBalance extends Fragment implements NewBalanceListener {
private TextView mybalance_value;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//Other onCreateView related code
Bundle bundle = this.getArguments();
if (bundle != null)
mybalance_value.setText(bundle.getString(Keys.BALANCE.toString(),
"0.00"));
else
mybalance_value.setText("0.00");
//Other onCreateView related code
}
#Override
public void onNewBalanceRead(String newBalance) {
mybalance_value.setText(newBalance);
}
}
This code works perfectly like expected for my application but, I want to know if there is better way to handle new Intent from Fragments?
This is an old question, but let me answer it in case anybody bumps into it.
First of all you have a bug in your code:
You can't register Fragments as listeners inside Activity the way you do it. The reason is that Activity and Fragments can be destroyed by the system and re-created later from saved state (see documentation on Recreating an Activity). When this happens, new instances of both the Activity and the Fragment will be created, but the code that sets the Fragment as a listener will not run, therefore onNewBalanceRead() will never be called. This is very common bug in Android applications.
In order to communicate events from Activity to Fragment I see at least two possible approaches:
Interface based:
There is an officially recommended approach for communication between Fragments. This approach is similar to what you do now in that it uses callback interfaces implemented by either Fragment or Activity, but its drawback is a tight coupling and lots of ugly code.
Event bus based:
The better approach (IMHO) is to make use of event bus - "master component" (Activity in your case) posts "update" events to event bus, whereas "slave component" (Fragment in your case) registers itself to event bus in onStart() (unregisters in onStop()) in order to receive these events. This is a cleaner approach which doesn't add any coupling between communicating components.
All my projects use Green Robot's EventBus, and I can't recommend it highly enough.
There is at least one alternative: From Activity.onNewIntent documentation:
An activity will always be paused before receiving a new intent, so you can count on onResume() being called after this method.
Note that getIntent() still returns the original Intent. You can use setIntent(Intent) to update it to this new Intent.
FragmentActivity.onNewIntent documentation is different but I don't think it contradicts the above statements. I also make the assumption that Fragment.onResume will be called after FragmentActivity.onResume, even though the documentation seems a little fussy to me, though my tests confirm this assumption. Based on this I updated the Intent in the activity like so (examples in Kotlin)
override fun onNewIntent(intent: Intent?) {
setIntent(intent)
super.onNewIntent(intent)
}
And in Fragment.onResume I could handle the new intent like so
override fun onResume() {
super.onResume()
doStuff(activity.intent)
}
This way the activity don't need to know about what fragments it holds.
No, there is no better way. Fragments can live longer than Activities and are not necessarily tied to them at all so providing new intents would not make sense.
Btw, you have a few bugs in your code :)
if (actionBar.getSelectedNavigationIndex() != 1) {
Magic numbers are bad! use a constant.
if (myBalanceBundle == null)
myBalanceBundle = new Bundle();
myBalanceBundle.putString(Keys.BALANCE.toString(), balance);
actionBar.setSelectedNavigationItem(1);
we already know that the navigationitem is set to 1
} else {
newBlanceListener.onNewBalanceRead(balance);
Add a null check. The user might have never selected a navigation item.

Categories

Resources