Getting Toolbar's title with Android's UI integration test framework - android

I have an fragment which set's the title to the entity the user is currently editing as follows:
Activity activity = this.getActivity();
CollapsingToolbarLayout appBarLayout = (CollapsingToolbarLayout) activity.findViewById(R.id.toolbar_layout);
if (appBarLayout != null) {
appBarLayout.setTitle(mItem.name);
}
In the testing code I successfully navigate to the fragment, however I'm unable to locate the element by the text as follows:
class DetailWrapper {
protected UiObject2 find( BySelector selector ){
device.wait(Until.findObject(selector), 2 * 1000);
return device.findObject(selector);
}
public ItemView hasName(String itemName) throws Exception {
UiObject2 title = find(By.text(itemName));
assertEquals(title.getText(), itemName);
return this;
}
}
title in hasName(String) is always null in the assert despite being on the correct activity with the correct title. What is the best method to assert the AppBar's title?

Related

Opening bottom sheet when clicked on the overflow menu on a fragment

I am trying to implement behavior of opening bottom sheet when clicked on overflow menu. ex: expected behavior
I may do this on an activity using onMenuOpened as suggested here,
But I want to do this on fragment.
How to achieve this behavior on a fragment?
I am using single activity pattern and navigation architecture component.
Create a interface which will be implemented by your Fragment's
ex:
public interface OnMenuOpenListener(){
boolean onMenuOpened();
}
public class MyFragment extends Fragment implements OnMenuOpenListener{
#Override
public boolean onMenuOpened(){
//open bottom sheet here
}
}
public class MyActivity extends Activity{
#Override
public boolean onMenuOpened(int featureId, Menu menu) {
if(featureId == AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR && menu != null){
//overflow menu clicked, put code here...
// As you are using navigation component
Fragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host);
//MyFragment
Fragment fragment=navHostFragment.getChildFragmentManager().getFragments().get(0);
if(fragment instanceof OnMenuOpenListener){
((OnMenuOpenListener)fragment).onMenuOpened()
return false;
}
}
return super.onMenuOpened(featureId, menu);
}
}
As Support Action Bar is attached to Activity All the event's are captured by Activity all you need to do is get the Fragment which need's the event and trigger the call using a call back.If you return false onMenuOpened will not open the overflow menu and will trigger bottom sheet menu from your fragment.
P.S- I have not written the code in Editor so might have some error's but you must have got the idea.
Reference:
https://stackoverflow.com/a/51732378/7972699
As discussed here opening bottom sheet when clicked on the overflow menu is bad UX.
Why?
Quoting from the post
Because user have to reach the top of the screen to click the oveflow
menu, then go back to the bottom to click desired action which is on
the bottom sheet.
-
According to Fitt's Law - The time to acquire a target is a function
of the distance to and size of the target. I agree that I think
distance between the menu and the bottom sheet is substantial. This
solution allows placing a lot options in one place.
-
it also doesn't match the user expectation since people are used to
the overflow menu opening in a different manner.
If you have a top action bar, use usual context menu. If you have a bottom action bar you may use bottom sheet.
**You can try the following steps to open bottom sheet dialog:**
1. Just make a function inside Activity where the fragment is replace
public Fragment getCurrentFragment() {
return getSupportFragmentManager().findFragmentById(R.id.frameContainer);
}
Fragment fragment = getCurrentFragment();
if (fragment != null) {
if (fragment instanceof RequiredFragment) {
RequiredFragment.openBottumSheetDialog();
}
}
2. In Side RequiredFragment get your function from activity:
private BottomSheetDialog mBottomSheetDialogFragment;
private void showBottomSheetFilter() {
if (mBottomSheetDialogFragment == null) {
mBottomSheetDialogFragment = mBottomSheetDialogFragment.newInstance(feedSection);
mBottomSheetDialogFragment.setCallBackListener(new OnFeedsTypeSelectedListener() {
#Override
public void onFeedsTypeSelected(int contentType) {
filterByContentTypeId(contentType);
}
}
mBottomSheetDialogFragment.show(getChildFragmentManager(),
mBottomSheetDialogFragment.getTag());
}
3. Create a BottomSheetDialog Dialog fragment.
public class BottomSheetDialog extends BottomSheetDialogFragment {
private String[] feedsFilter;
private ListView listView;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
feedsFilter = getResources().getStringArray(R.array.ideas_filter);
}
#Override
public void setupDialog(final Dialog dialog, int style) {
super.setupDialog(dialog, style);
View contentView = View.inflate(getContext(), R.layout.dialog_idea_filter_bottom_sheet, null);
dialog.setContentView(contentView);
listView = (ListView) contentView.findViewById(R.id.listView);
ArrayAdapter < String > adapter = new ArrayAdapter < String > (getActivity(),
android.R.layout.simple_list_item_1, feedsFilter);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView < ? > parent, View view,
int position, long id) {
if (onFeedsTypeSelected != null) {
onIdeaTypeSelectedListenonFeedsTypeSelecteder.onFeedsTypeSelected(feedsFilter[position]);
}
dismiss();
}
});
}
public void setCallBackListener(onFeedsTypeSelected SelectedListener onFeedsTypeSelected) {
this.onIdeaTypeSelectedLionFeedsTypeSelectedstener = onFeedsTypeSelected;
}
}

Toolbar (SupportActionBar) title changes to app name on orientation change

I have this code in my Activity:
protected void onCreate(Bundle savedInstanceState) {
...
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
...
}
I'm updating the ActionBar title from various fragments like this in onResume():
ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(title);
}
This is working fine, but after orientation change, the title changes to the app name again. How I can overcome this?
EDIT:
After investigating more, I tried this and found this weird behaviour:
Added this code where I was setting title in Fragments:
final ActionBar actionBar = ((AppCompatActivity)getActivity()).getSupportActionBar();
if (actionBar != null) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Log.d("SWAPNIL", "IN RUN BEFORE: " + actionBar.getTitle());
actionBar.setTitle(title);
Log.d("SWAPNIL", "IN RUN AFTER : " + actionBar.getTitle());
}
}, 3000);
}
And here's the log:
10-13 10:27:04.526 3719-3719/com.example.xxxx D/SWAPNIL: onResumeHelp
10-13 10:27:07.528 3719-3719/com.example.xxxx D/SWAPNIL: IN RUN BEFORE: MY APP NAME
10-13 10:27:07.528 3719-3719/com.example.xxxx D/SWAPNIL: IN RUN AFTER : title
10-13 10:27:21.012 3719-3719/com.example.xxxx D/SWAPNIL: onResumeHelp
10-13 10:27:24.013 3719-3719/com.example.xxxx D/SWAPNIL: IN RUN BEFORE: title
10-13 10:27:24.013 3719-3719/com.example.xxxx D/SWAPNIL: IN RUN AFTER : title
It was getting changed as per logs but wasn't reflected in UI.
Please help me!
Okay, finally, after spending 2 days on this silly thing, I got the solution (I would say workaround).
This is probably a nested Fragment bug.
I have nested Fragment structure. As we know Fragment.getActivity() returns parent Activity. After lot of debugging I observed that if you call getActivity() after orientation change (even inside Fragment.onActivityCreated()) it returns reference of the old Activity except in top most parent fragment where it correctly returns the newly created Activity.
So I've written this method to get current Activity from any Fragment:
/**
* When inside a nested fragment and Activity gets recreated due to reasons like orientation
* change, {#link android.support.v4.app.Fragment#getActivity()} returns old Activity but the top
* level parent fragment's {#link android.support.v4.app.Fragment#getActivity()} returns current,
* recreated Activity. Hence use this method in nested fragments instead of
* android.support.v4.app.Fragment#getActivity()
*
* #param fragment
* The current nested Fragment
*
* #return current Activity that fragment is hosted in
*/
public Activity getActivity(Fragment fragment) {
if (fragment == null) {
return null;
}
while (fragment.getParentFragment() != null) {
fragment = fragment.getParentFragment();
}
return fragment.getActivity();
}
You need to see the answer written by sorianiv here:
In android app Toolbar.setTitle method has no effect – application name is shown as title
Adding the additional toolbar.setTitle("") resolved this issue for me.
Toolbar toolbar = (Toolbar) root.findViewById(R.id.toolbar);
toolbar.setTitle("");
((AppCompatActivity)getActivity()).setSupportActionBar(toolbar);
toolbar.setTitle("Profiles");
I just ran into the same issue, and I solved it by using getSupportActionBar instead of toolbar.
getSupportActionBar().setTitle("...");
You're activity is getting recreated when you rotate the screen. So onCreate is getting called again, and you have not set a title there.
Add the following to your onCreate:
actionBar.setTile("Enter title here")
maybe you can change your Action Bar / ToolBar, in onPostCreate() method in your Activity..
The code is just simple. I try to change my emulator / device orientation for many times and it works like charm..
#Override
protected void onPostCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onPostCreate(savedInstanceState);
toolbar.setTitle(mTitle);
}
Add in Manifest file
<activity
android:name=".Activity"
android:configChanges="orientation|screenSize|keyboardHidden"/>
or
code in onConfigChange()
if(newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE){
// Set Title for Landscape
}else{
// Set Title for Portrait

CollapsingToolbarLayout setTitle() doesn't work after activity created

In my activity I've created a method for the fragments to update the title :
public void setTitle(String title) {
ActionBar ab = getSupportActionBar();
if (ab != null) {
if (title == null) {
title = getString(R.string.string_title);
}
ab.setTitle(title);
}
}
The toolbar is part of a CollapsingToolbarLayout.
It looks like the title is set correctly for the first fragment, however, when the second one tries to update it, it doesn't get updated.
I'm running with v 23 so this is not the known bug.

Up navigation not appearing for single activity app with multiple fragments

I have an android application that has a single main activity that employs many fragments that switch into view. I'm not sure if that's the right way to do it, but I have inherited this project and would like to avoid doing any major refactors like changing the fragments to all be activities or something like that.
According to the android documentation, it looks like calling the setDisplayHomeAsUp(bool) function should just display the up button by default:
Set whether home should be displayed as an "up" affordance. Set this
to true if selecting "home" returns up by a single level in your UI
rather than back to the top level or front page.
The main issue is that when I use the function:
actionBar.setDisplayHomeAsUpEnabled(true);
It does not set the button that opens the navigation drawer to instead turn into an 'Up' button. It just removes the 'hamburger' ic_drawer icon from the side. The navigation drawer still opens.
Here is the custom code for the NavigationDrawerFragment (I copy+pasted the exact file that you get when you create a new application with a navigation drawer within android studio):
NavigationDrawerFragment.java
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
mDrawerListView = (ListView) inflater.inflate(
R.layout.fragment_navigation_drawer, container, false);
mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItem(position);
}
});
PopulateAppDrawerList();
return mDrawerListView;
}
public void PopulateAppDrawerList() {
List<AppOption> allApps = getAllApps();
List<AppOption> filteredApps = new ArrayList<AppOption>();
for (int i = 0; i < allApps.size(); i++) {
if (allApps.get(i).getLaunchable()) {
filteredApps.add(allApps.get(i));
}
}
NavDrawerListAdapter adapter = new NavDrawerListAdapter(filteredApps, MainActivity.getInstance());
mDrawerListView.setAdapter(adapter);
mDrawerListView.setItemChecked(mCurrentSelectedPosition, true);
}
Then, I have the other fragments that all extend 'BaseAppFragment', which contains the following:
BaseAppFragment.java
public class BaseAppFragment extends Fragment {
#Override
public void onStart() {
super.onStart();
MainActivity.getInstance().onSectionAttached(this);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
} }
This is what allows me to change the title on the action bar in one single area and set whether or not it should have the back button set by default.
MainActivity.java
public void onSectionAttached(android.app.Fragment fragment) {
Class fragmentType = fragment.getClass();
ActionBar actionBar = getActionBar();
mNavigationDrawerFragment.PopulateAppDrawerList();
if (fragmentType != null) {
if (fragmentType.equals(AuthenticationFragment.class)) {
actionBar.setDisplayHomeAsUpEnabled(false);
mTitle = "Login";
} else if (fragmentType.equals(MyOptionsFragment.class)) {
actionBar.setDisplayHomeAsUpEnabled(false);
mTitle = "My Options";
} else if (fragmentType.equals(GLAuthenticationFragment.class)) {
actionBar.setDisplayHomeAsUpEnabled(false);
mTitle = "Login";
} else if (fragmentType.equals(InitialLoginFragment.class)) {
actionBar.setDisplayHomeAsUpEnabled(false);
mTitle = "Login";
} else if (fragmentType.equals(LoginFragment.class)) {
actionBar.setDisplayHomeAsUpEnabled(false);
mTitle = "Login";
} else if (fragmentType.equals(DailyOverviewFragment.class)) {
actionBar.setDisplayHomeAsUpEnabled(true);
mTitle = "Overview";
} else if (fragmentType.equals(SingleComponentFragment.class)) {
actionBar.setDisplayHomeAsUpEnabled(true);
SingleComponentFragment singleComponentFragment = (SingleComponentFragment) fragment;
if (singleComponentFragment != null && singleComponentFragment .mComponent != null) {
mTitle = String.format("Back To Day %s", singleComponentFragment.mComponent.getDay() + "");
}
else {
mTitle = "";
}
} else if (fragmentType.equals(singleDayOverviewFragment.class)) {
actionBar.setDisplayHomeAsUpEnabled(true);
mTitle = "Back To Overview";
}
}
actionBar.setTitle(mTitle);
}
The title setting works perfectly and there are no errors when the setDisplayHomeAsUpEnabled(true) is called, but it still shows no Up button. I know that right now I am not setting any type of fragment navigation hierarchy other than the addToBackStack(null) call in the Fragment Transaction, but it still seems like this code should be enough to have the up button replace the navigation drawer button.
The problem is that the navigation drawer icon hijacks the up indicator. In terms of which View in the action bar is displaying the icon, the navigation drawer icon is also the up icon. This is why you need to call actionBar.setDisplayHomeAsUpEnabled(true); for the navigation drawer icon to show.
To fix this, you need to use ActionBarDrawerToggle#setDrawerIndicatorEnabled(false). This will replace the navigation drawer icon with the up icon. From the documentation for this method:
When the indicator is disabled, the ActionBar will revert to displaying the home-as-up indicator provided by the Activity's theme in the android.R.attr.homeAsUpIndicator attribute instead of the animated drawer glyph.
Had the same problem as you, and I struggled getting a consistent Up arrow behaviour. I made this example to show how to properly do it when using a single activity with navigation drawer and multiple levels.
https://github.com/tskulbru/android-navdrawer-up-pattern-example

Android: How to determine which fragment the view with focus resides in

I am new to Android and I haven't been able to figure this one out yet.
I have a fragment which is reused multiple times in my app. I need to identify which fragment contains the view with focus when a menu item is clicked so I can update an EditText view in that fragment. How do I accomplish this?
In your container(parent) activity, you can check whether that fragment is null:
MyFragment fragment = (MyFragment) getFragmentManager().findFragmentById(R.id.myfragment_id);
if(fragment != null)
{
//this fragment is used currently
}
or you can put a variable ,set that variable according to current fragment.
in your parent activity:
private TextView mFocusedText = null;
public void textViewGotFocus(TextView tv) {
if(tv != null) mFocusedText = tv;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if(mFocusedText != null) mFocusedText.setText(item.getTitle());
}
in your fragments ad a focus listener on a item your choice or multiples and call
getActivity().textViewGotFocus(myFragmentTextView);
it isnt tested but it should work this way
good luck

Categories

Resources