Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(android): Fix setting an active tab for TabGroup before opening. #10740

Merged
merged 9 commits into from
Mar 8, 2019
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
import android.os.Message;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.WindowManager;

// clang-format off
@Kroll.proxy(creatableInModule = UIModule.class,
propertyAccessors = {
Expand Down Expand Up @@ -445,14 +445,10 @@ protected void handlePostOpen()

TabProxy activeTab = handleGetActiveTab();
if (activeTab != null) {
selectedTab = activeTab;
selectedTab = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class' onTabSelected() method is missing a null check on the selectedTab member variable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you referring to this onTabSelected() method?
https://github.com/appcelerator/titanium_mobile/blob/master/android/modules/ui/src/java/ti/modules/titanium/ui/TabGroupProxy.java#L529
If that is the case, it is intended for the selectedTab to be null in order to skip the sending of the focus/blur events when a tab is set as an active one before the opening of the TabGroup. But I may have not got what you mean.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh you're right. Never mind.

Copy link
Contributor

@sgtcoolguy sgtcoolguy Mar 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the unit test around blur events is now failing, and is likely related to the changes in this area...

https://github.com/appcelerator/titanium-mobile-mocha-suite/blob/master/Resources/ti.ui.tabgroup.test.js#L232-L247

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After some digging I found that the "blur" unit test started failing because this PR fixed firing a pair of focus/blur events that are not supposed to be fired. That problem was introduced at the same time when the unit test was added (Both by me) and this is the reason it didn't fail every single time in other PRs. With my latest update this should be cleared up.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The unit test is passing now.

// If tabHost's selected tab is same as the active tab, we need
// to invoke onTabSelected so focus/blur event fire appropriately
if (tg.getSelectedTab() == activeTab) {
onTabSelected(activeTab);
} else {
tg.selectTab(activeTab);
}
tg.selectTab(activeTab);
}

// Selected tab should have been focused by now.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@
import org.appcelerator.titanium.TiC;
import org.appcelerator.titanium.proxy.TiViewProxy;
import org.appcelerator.titanium.proxy.TiWindowProxy;
import org.appcelerator.titanium.util.TiConvert;
import org.appcelerator.titanium.util.TiUIHelper;
import org.appcelerator.titanium.view.TiUIView;

import ti.modules.titanium.ui.widget.tabgroup.TiUITab;
import android.app.Activity;
// clang-format off
@Kroll.proxy(creatableInModule = UIModule.class,
Expand Down Expand Up @@ -216,8 +214,6 @@ void onSelectionChanged(boolean selected)
TiUIHelper.showSoftKeyboard(currentActivity.getWindow().getDecorView(), false);
}
}

((TiUITab) view).onSelectionChange(selected);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,15 +168,16 @@ public void disableTabNavigation(boolean value)

/**
* Method for handling of the setActiveTab. It is used to set the currently selected page
* throw the code and not clicking/swiping.
* through the code and not clicking/swiping.
* @param tabProxy the TabProxy instance to be set as currently selected
*/
public void selectTab(TabProxy tabProxy)
{
int index = ((TabGroupProxy) getProxy()).getTabList().indexOf(tabProxy);
// Guard for trying to set a tab, that is not part of the group, as active.
if (index != -1 && !tabsDisabled) {
this.tabGroupViewPager.setCurrentItem(index, this.smoothScrollOnTabClick);
selectTabItemInController(index);
selectTab(index);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,6 @@ public void addTabItemInController(TabProxy tabProxy)
}
// Add the MenuItem to the menu of BottomNavigationView.
this.mMenuItemsArray.add(menuItem);
// TabLayout automatically select the first tab that is added to it,
// but BottomNavigationView does not do that, so we manually trigger it.
// That's necessary to match the behavior across styles.
if (this.mMenuItemsArray.size() == 1) {
((TabGroupProxy) getProxy()).onTabSelected(tabProxy);
currentlySelectedIndex = 0;
selectTab(currentlySelectedIndex);
}
// Set the drawables.
setDrawables();
// Handle shift mode.
Expand Down Expand Up @@ -256,10 +248,12 @@ public void removeTabItemFromController(int position)
public void selectTabItemInController(int position)
{
// Fire the UNSELECTED event from the currently selected tab.
((TabGroupProxy) getProxy())
.getTabList()
.get(currentlySelectedIndex)
.fireEvent(TiC.EVENT_UNSELECTED, null, false);
if (currentlySelectedIndex != -1) {
((TabGroupProxy) getProxy())
.getTabList()
.get(currentlySelectedIndex)
.fireEvent(TiC.EVENT_UNSELECTED, null, false);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should check if getProxy() returns null in the selectTabItemInController() method and the onMenuItemClick() method, which can happen when the proxy has been released (ie: it's JavaScript object was garbage collected).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

currentlySelectedIndex = position;
// The ViewPager has changed current page from swiping.
// Or any other interaction with it that can cause page change.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,6 @@ public TiUITab(TabProxy proxy)
proxy.setView(this);
}

/**
* Called when the selection of this tab has changed.
*
* @param selected true if the tab is now selected or false if it was unselected.
*/
public void onSelectionChange(boolean selected)
{
}

/**
* Returns the content view for this tab.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import android.graphics.Rect;
import android.support.design.widget.TabLayout;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

Expand Down Expand Up @@ -156,7 +155,7 @@ public void addTabItemInController(TabProxy tabProxy)
newTab.setIcon(drawable);
}
// Add the new tab to the TabLayout.
this.mTabLayout.addTab(newTab);
this.mTabLayout.addTab(newTab, false);

// Create a background drawable with ripple effect for the state used by TabLayout.Tab.
Drawable backgroundDrawable = createBackgroundDrawableForState(tabProxy, android.R.attr.state_selected);
Expand Down
2 changes: 1 addition & 1 deletion apidoc/Titanium/UI/TabGroup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ methods:
properties:
- name: activeTab
summary: Active tab.
type: Titanium.UI.Tab
type: [Number, Titanium.UI.Tab]

- name: activity
summary: |
Expand Down
197 changes: 197 additions & 0 deletions tests/Resources/ti.ui.tabgroup.addontest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/*
* Appcelerator Titanium Mobile
* Copyright (c) 2011-Present by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Apache Public License
* Please see the LICENSE included with this distribution for details.
*/
/* eslint-env mocha */
/* global Ti */
/* eslint no-unused-expressions: "off" */
'use strict';
const should = require('./utilities/assertions'); // eslint-disable-line no-unused-vars

// skipping many test on Windows due to lack of event firing, see https://jira.appcelerator.org/browse/TIMOB-26690
describe('Titanium.UI.TabGroup', () => {
let tabGroup;
let tab;

afterEach(() => {
if (tabGroup) {
if (tab) {
tabGroup.removeTab(tab);
}
tabGroup.close();
tabGroup = null;
}
tab = null;
});

it.windowsBroken('#setActiveTab()', finish => {
const winA = Ti.UI.createWindow(),
tabA = Ti.UI.createTab({
title: 'Tab A',
window: winA
}),
winB = Ti.UI.createWindow(),
tabB = Ti.UI.createTab({
title: 'Tab B',
window: winB
});
tabGroup = Ti.UI.createTabGroup();

// Does windows fire this event?
// Can we test this without even opening tab group?
tabGroup.addEventListener('open', () => {
try {
tabGroup.setActiveTab(tabB);
should(tabGroup.getActiveTab().title).be.a.String;
should(tabGroup.getActiveTab().title).eql('Tab B');
finish();
} catch (err) {
finish(err);
} finally {
tabGroup.removeTab(tabA);
tabGroup.removeTab(tabB);
}
});

tabGroup.addTab(tabA);
tabGroup.addTab(tabB);
tabGroup.open();
});

it.windowsBroken('#setActiveTab()_before_open', finish => {
const winA = Ti.UI.createWindow(),
tabA = Ti.UI.createTab({
title: 'Tab A',
window: winA
}),
winB = Ti.UI.createWindow(),
tabB = Ti.UI.createTab({
title: 'Tab B',
window: winB
});
tabGroup = Ti.UI.createTabGroup();
tabGroup.setActiveTab(1);
// Does windows fire this event?
// Can we test this without even opening tab group?
tabGroup.addEventListener('open', () => {
try {
should(tabGroup.getActiveTab().title).be.a.String;
should(tabGroup.getActiveTab().title).eql('Tab B');
finish();
} catch (err) {
finish(err);
} finally {
tabGroup.removeTab(tabA);
tabGroup.removeTab(tabB);
}
});

tabGroup.addTab(tabA);
tabGroup.addTab(tabB);
tabGroup.open();
});

it.windowsBroken('#change_activeTab_property', finish => {
const winA = Ti.UI.createWindow(),
tabA = Ti.UI.createTab({
title: 'Tab A',
window: winA
}),
winB = Ti.UI.createWindow(),
tabB = Ti.UI.createTab({
title: 'Tab B',
window: winB
});
tabGroup = Ti.UI.createTabGroup();

// Does windows fire this event?
// Can we test this without even opening tab group?
tabGroup.addEventListener('open', () => {
try {
tabGroup.activeTab = tabB;
should(tabGroup.getActiveTab().title).be.a.String;
should(tabGroup.getActiveTab().title).eql('Tab B');
finish();
} catch (err) {
finish(err);
} finally {
tabGroup.removeTab(tabA);
tabGroup.removeTab(tabB);
}
});

tabGroup.addTab(tabA);
tabGroup.addTab(tabB);
tabGroup.open();
});

it.windowsBroken('#change_activeTab_property_before_open', finish => {
const winA = Ti.UI.createWindow(),
tabA = Ti.UI.createTab({
title: 'Tab A',
window: winA
}),
winB = Ti.UI.createWindow(),
tabB = Ti.UI.createTab({
title: 'Tab B',
window: winB
});
tabGroup = Ti.UI.createTabGroup();
tabGroup.activeTab = 1;
// Does windows fire this event?
// Can we test this without even opening tab group?
tabGroup.addEventListener('open', () => {
try {
should(tabGroup.getActiveTab().title).be.a.String;
should(tabGroup.getActiveTab().title).eql('Tab B');
finish();
} catch (err) {
finish(err);
} finally {
tabGroup.removeTab(tabA);
tabGroup.removeTab(tabB);
}
});

tabGroup.addTab(tabA);
tabGroup.addTab(tabB);
tabGroup.open();
});

it.windowsBroken('#set_activeTab_in_creation_dictionary', finish => {
const winA = Ti.UI.createWindow(),
tabA = Ti.UI.createTab({
title: 'Tab A',
window: winA
}),
winB = Ti.UI.createWindow(),
tabB = Ti.UI.createTab({
title: 'Tab B',
window: winB
});
tabGroup = Ti.UI.createTabGroup({
activeTab: 1
});
// Does windows fire this event?
// Can we test this without even opening tab group?
tabGroup.addEventListener('open', () => {
try {
should(tabGroup.getActiveTab().title).be.a.String;
should(tabGroup.getActiveTab().title).eql('Tab B');
finish();
} catch (err) {
finish(err);
} finally {
tabGroup.removeTab(tabA);
tabGroup.removeTab(tabB);
}
});

tabGroup.addTab(tabA);
tabGroup.addTab(tabB);
tabGroup.open();
});

});