From cee32ae14a142c53eca6c8e0e5ed64098098e522 Mon Sep 17 00:00:00 2001 From: Michael Gangolf Date: Fri, 27 Sep 2024 14:52:48 +0200 Subject: [PATCH 1/6] xml test --- .../layout/titanium_ui_bottom_navigation.xml | 31 ++++ .../titanium/ui/BottomNavigationProxy.java | 142 ++++++++++++++++++ .../ui/widget/TiUIBottomNavigation.java | 98 ++++++++++++ 3 files changed, 271 insertions(+) create mode 100644 android/modules/ui/res/layout/titanium_ui_bottom_navigation.xml create mode 100644 android/modules/ui/src/java/ti/modules/titanium/ui/BottomNavigationProxy.java create mode 100644 android/modules/ui/src/java/ti/modules/titanium/ui/widget/TiUIBottomNavigation.java diff --git a/android/modules/ui/res/layout/titanium_ui_bottom_navigation.xml b/android/modules/ui/res/layout/titanium_ui_bottom_navigation.xml new file mode 100644 index 00000000000..3f4e369166d --- /dev/null +++ b/android/modules/ui/res/layout/titanium_ui_bottom_navigation.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/BottomNavigationProxy.java b/android/modules/ui/src/java/ti/modules/titanium/ui/BottomNavigationProxy.java new file mode 100644 index 00000000000..3bb33a47aeb --- /dev/null +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/BottomNavigationProxy.java @@ -0,0 +1,142 @@ +package ti.modules.titanium.ui; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; + +import org.appcelerator.kroll.KrollDict; +import org.appcelerator.kroll.annotations.Kroll; +import org.appcelerator.kroll.common.Log; +import org.appcelerator.titanium.TiActivity; +import org.appcelerator.titanium.TiActivityWindow; +import org.appcelerator.titanium.TiActivityWindows; +import org.appcelerator.titanium.TiApplication; +import org.appcelerator.titanium.TiBaseActivity; +import org.appcelerator.titanium.TiC; +import org.appcelerator.titanium.TiRootActivity; +import org.appcelerator.titanium.proxy.TiWindowProxy; +import org.appcelerator.titanium.util.TiConvert; + +import java.lang.ref.WeakReference; + +import ti.modules.titanium.ui.widget.TiUIBottomNavigation; + +@Kroll.proxy(creatableInModule = UIModule.class, propertyAccessors = {}) +public class BottomNavigationProxy extends TiWindowProxy implements TiActivityWindow +{ + private static final String TAG = "BottomNavigation"; + Object tabs; + private TiUIBottomNavigation bottomNavigation; + private WeakReference bottomNavigationActivity = new WeakReference<>(null); + + @Override + protected void handleOpen(KrollDict options) + { + Activity topActivity = TiApplication.getAppCurrentActivity(); + // Don't open if app is closing or closed + if (topActivity == null || topActivity.isFinishing()) { + return; + } + Intent intent = new Intent(topActivity, TiActivity.class); + fillIntent(topActivity, intent); + + int windowId = TiActivityWindows.addWindow(this); + intent.putExtra(TiC.INTENT_PROPERTY_WINDOW_ID, windowId); + + boolean animated = TiConvert.toBoolean(options, TiC.PROPERTY_ANIMATED, true); + if (!animated) { + intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + topActivity.startActivity(intent); + topActivity.overridePendingTransition(0, 0); + } else if (options.containsKey(TiC.PROPERTY_ACTIVITY_ENTER_ANIMATION) + || options.containsKey(TiC.PROPERTY_ACTIVITY_EXIT_ANIMATION)) { + topActivity.startActivity(intent); + int enterAnimation = TiConvert.toInt(options.get(TiC.PROPERTY_ACTIVITY_ENTER_ANIMATION), 0); + int exitAnimation = TiConvert.toInt(options.get(TiC.PROPERTY_ACTIVITY_EXIT_ANIMATION), 0); + topActivity.overridePendingTransition(enterAnimation, exitAnimation); + } else { + topActivity.startActivity(intent); + if (topActivity instanceof TiRootActivity) { + // A fade-in transition from root splash screen to first window looks better than a slide-up. + // Also works-around issue where splash in mid-transition might do a 2nd transition on cold start. + topActivity.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); + } + } + } + + @Override + protected void handleClose(@NonNull KrollDict options) + { + Log.d(TAG, "handleClose: " + options, Log.DEBUG_MODE); + + // Remove this TabGroup proxy from the active/open collection. + // Note: If the activity's onCreate() can't find this proxy, then it'll automatically destroy itself. + // This is needed in case the proxy's close() method was called before the activity was created. + TiActivityWindows.removeWindow(this); + + // Release views/resources. + modelListener = null; + releaseViews(); + view = null; + + // Destroy this proxy's activity. + AppCompatActivity activity = (bottomNavigationActivity != null) ? bottomNavigationActivity.get() : null; + bottomNavigationActivity = null; + if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) { + activity.finish(); + } + + // NOTE: this does not directly fire the close event, but is fired by closeFromActivity() + } + + @Override + protected Activity getWindowActivity() + { + return (bottomNavigationActivity != null) ? bottomNavigationActivity.get() : null; + } + + @Override + public void windowCreated(TiBaseActivity activity, Bundle savedInstanceState) + { + bottomNavigationActivity = new WeakReference<>(activity); + activity.setWindowProxy(this); + activity.setLayoutProxy(this); + setActivity(activity); + + view = new TiUIBottomNavigation(this, activity); + setModelListener(view); + } + + @Override + public void handleCreationDict(KrollDict options) + { + super.handleCreationDict(options); + + // Support setting orientation modes at creation. + Object orientationModes = options.get(TiC.PROPERTY_ORIENTATION_MODES); + if (orientationModes instanceof Object[]) { + try { + int[] modes = TiConvert.toIntArray((Object[]) orientationModes); + setOrientationModes(modes); + + } catch (ClassCastException e) { + Log.e(TAG, "Invalid orientationMode array. Must only contain orientation mode constants."); + } + } + + if (options.containsKeyAndNotNull(TiC.PROPERTY_TABS)) { + tabs = options.get(TiC.PROPERTY_TABS); + TiUIBottomNavigation tabGroup = (TiUIBottomNavigation) view; + if (tabGroup != null) { + tabGroup.setTabs((Object[]) tabs); + } + } + if (options.containsKeyAndNotNull(TiC.PROPERTY_THEME)) { + Log.i("---", "a Theme: " + options.get(TiC.PROPERTY_THEME)); + } + + } +} diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/TiUIBottomNavigation.java b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/TiUIBottomNavigation.java new file mode 100644 index 00000000000..ee4c39d8de5 --- /dev/null +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/TiUIBottomNavigation.java @@ -0,0 +1,98 @@ +package ti.modules.titanium.ui.widget; + +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.widget.RelativeLayout; + +import androidx.annotation.NonNull; + +import com.google.android.material.bottomnavigation.BottomNavigationView; + +import org.appcelerator.titanium.R; +import org.appcelerator.titanium.TiApplication; +import org.appcelerator.titanium.TiBaseActivity; +import org.appcelerator.titanium.proxy.TiViewProxy; +import org.appcelerator.titanium.util.TiRHelper; +import org.appcelerator.titanium.view.TiUIView; + +import java.util.ArrayList; + +public class TiUIBottomNavigation extends TiUIView implements BottomNavigationView.OnItemSelectedListener +{ + private static final String TAG = "BottomNavigation"; + static int id_layout = 0; + static int id_content = 0; + static int id_bottomNavigation = 0; + private final ArrayList mMenuItemsArray = new ArrayList<>(); + private RelativeLayout layout = null; + private TiViewProxy centerView; + private BottomNavigationView bottomNavigation; + + public TiUIBottomNavigation(TiViewProxy proxy, TiBaseActivity activity) + { + super(proxy); + try { + id_layout = TiRHelper.getResource("layout.titanium_ui_bottom_navigation"); + id_content = TiRHelper.getResource("id.bottomNavBar_content"); + id_bottomNavigation = TiRHelper.getResource("id.bottomNavBar"); + + LayoutInflater inflater = LayoutInflater.from(TiApplication.getAppRootOrCurrentActivity()); + layout = (RelativeLayout) inflater.inflate(id_layout, null, false); + bottomNavigation = layout.findViewById(id_bottomNavigation); + + bottomNavigation.setOnItemSelectedListener(this); + + activity.setLayout(layout); + setTabs(new Object[2]); + } catch (Exception ex) { + Log.e(TAG, "XML resources could not be found!!!" + ex.getMessage()); + } + + } + + @Override + public boolean onNavigationItemSelected(@NonNull MenuItem item) + { + Log.i("----", "Selected item: " + item.getItemId()); + return false; + } + + public void setTabs(Object[] tabs) + { + Log.i("---", "hier"); + for (Object tabView : tabs) { + Log.i("---", "1"); + MenuItem menuItem = bottomNavigation.getMenu().add(0, this.mMenuItemsArray.size(), 0, ""); + menuItem.setTitle("test"); + menuItem.setIcon(R.drawable.titanium_icon_checkcircle); + // Set the click listener. + //menuItem.setOnMenuItemClickListener(this); + // Add the MenuItem to the menu of BottomNavigationView. + this.mMenuItemsArray.add(menuItem); + } + } + +// public void setContent(TiViewProxy viewProxy) +// { +// if (viewProxy == null || viewProxy == this.centerView) { +// return; +// } +// +// viewProxy.setActivity(proxy.getActivity()); +// TiUIView contentView = viewProxy.getOrCreateView(); +// +// View view = contentView.getOuterView(); +// LinearLayout container = (LinearLayout) layout.findViewById(id_content); +// TiCompositeLayout content = (TiCompositeLayout) container.getChildAt(1); +// ViewParent viewParent = view.getParent(); +// if (viewParent != null && viewParent != content && viewParent instanceof ViewGroup) { +// ((ViewGroup) viewParent).removeView(view); +// } +// content.addView(view, contentView.getLayoutParams()); +// if (this.centerView != null) { +// content.removeView(this.centerView.getOrCreateView().getNativeView()); +// } +// this.centerView = viewProxy; +// } +} From 4d7916b38e7e65f6c9effa8b1d18bcdbcd700b7b Mon Sep 17 00:00:00 2001 From: Michael Gangolf Date: Sun, 29 Sep 2024 14:44:56 +0200 Subject: [PATCH 2/6] badge --- .../layout/titanium_ui_bottom_navigation.xml | 15 +- .../titanium/ui/BottomNavigationProxy.java | 26 ++-- .../java/ti/modules/titanium/ui/TabProxy.java | 16 +++ .../ui/widget/TiUIBottomNavigation.java | 128 ++++++++++++------ 4 files changed, 123 insertions(+), 62 deletions(-) diff --git a/android/modules/ui/res/layout/titanium_ui_bottom_navigation.xml b/android/modules/ui/res/layout/titanium_ui_bottom_navigation.xml index 3f4e369166d..9a50ac07b42 100644 --- a/android/modules/ui/res/layout/titanium_ui_bottom_navigation.xml +++ b/android/modules/ui/res/layout/titanium_ui_bottom_navigation.xml @@ -7,20 +7,7 @@ android:id="@+id/bottomNavBar_content" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_above="@+id/bottomNavBar"> - - - - + android:layout_above="@+id/bottomNavBar" /> mMenuItemsArray = new ArrayList<>(); private RelativeLayout layout = null; - private TiViewProxy centerView; + private FrameLayout centerView; private BottomNavigationView bottomNavigation; + private Object[] tabsArray; public TiUIBottomNavigation(TiViewProxy proxy, TiBaseActivity activity) { @@ -40,59 +49,100 @@ public TiUIBottomNavigation(TiViewProxy proxy, TiBaseActivity activity) LayoutInflater inflater = LayoutInflater.from(TiApplication.getAppRootOrCurrentActivity()); layout = (RelativeLayout) inflater.inflate(id_layout, null, false); bottomNavigation = layout.findViewById(id_bottomNavigation); + centerView = layout.findViewById(id_content); bottomNavigation.setOnItemSelectedListener(this); - activity.setLayout(layout); - setTabs(new Object[2]); + + if (proxy.hasProperty(TiC.PROPERTY_TABS)) { + setTabs(proxy.getProperty(TiC.PROPERTY_TABS)); + selectTab(0); + } + } catch (Exception ex) { Log.e(TAG, "XML resources could not be found!!!" + ex.getMessage()); } } + public void updateBadge(int index) + { + if ((index < 0) || (index >= tabsArray.length)) { + return; + } + + TiViewProxy tabProxy = ((TabProxy) tabsArray[index]); + if (tabProxy == null) { + return; + } + + Object badgeValue = tabProxy.getProperty(TiC.PROPERTY_BADGE); + if ((badgeValue == null) && !TiUIHelper.isUsingMaterialTheme(bottomNavigation.getContext())) { + return; + } + + int menuItemId = bottomNavigation.getMenu().getItem(index).getItemId(); + BadgeDrawable badgeDrawable = bottomNavigation.getOrCreateBadge(menuItemId); + if (badgeValue != null) { + badgeDrawable.setVisible(true); + badgeDrawable.setNumber(TiConvert.toInt(badgeValue, 0)); + } else { + badgeDrawable.setVisible(false); + } + } + + private void selectTab(int id) + { + TabProxy tp = ((TabProxy) tabsArray[id]); + if (tp != null) { + TiUITab abstractTab = new TiUITab(tp); + + centerView.removeAllViews(); + TiUIView view = abstractTab.getWindowProxy().getOrCreateView(); + if (view != null) { + centerView.addView(view.getOuterView()); + } + + } + } + @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { - Log.i("----", "Selected item: " + item.getItemId()); - return false; + item.setChecked(true); + selectTab(item.getItemId()); + return true; } - public void setTabs(Object[] tabs) + public void setTabs(Object tabs) { - Log.i("---", "hier"); - for (Object tabView : tabs) { - Log.i("---", "1"); - MenuItem menuItem = bottomNavigation.getMenu().add(0, this.mMenuItemsArray.size(), 0, ""); - menuItem.setTitle("test"); - menuItem.setIcon(R.drawable.titanium_icon_checkcircle); - // Set the click listener. - //menuItem.setOnMenuItemClickListener(this); - // Add the MenuItem to the menu of BottomNavigationView. - this.mMenuItemsArray.add(menuItem); + if (tabs instanceof Object[] objArray) { + tabsArray = objArray; + for (Object tabView : tabsArray) { + if (tabView instanceof TabProxy tp) { + MenuItem menuItem = bottomNavigation.getMenu().add(0, this.mMenuItemsArray.size(), 0, ""); + tp.setNavBar(this, menuItem.getItemId()); + menuItem.setTitle(tp.getProperty(TiC.PROPERTY_TITLE).toString()); + Drawable drawable = TiUIHelper.getResourceDrawable(tp.getProperty(TiC.PROPERTY_ICON)); + menuItem.setIcon(drawable); + this.mMenuItemsArray.add(menuItem); + } + } } } -// public void setContent(TiViewProxy viewProxy) -// { -// if (viewProxy == null || viewProxy == this.centerView) { -// return; -// } -// -// viewProxy.setActivity(proxy.getActivity()); -// TiUIView contentView = viewProxy.getOrCreateView(); -// -// View view = contentView.getOuterView(); -// LinearLayout container = (LinearLayout) layout.findViewById(id_content); -// TiCompositeLayout content = (TiCompositeLayout) container.getChildAt(1); -// ViewParent viewParent = view.getParent(); -// if (viewParent != null && viewParent != content && viewParent instanceof ViewGroup) { -// ((ViewGroup) viewParent).removeView(view); -// } -// content.addView(view, contentView.getLayoutParams()); -// if (this.centerView != null) { -// content.removeView(this.centerView.getOrCreateView().getNativeView()); -// } -// this.centerView = viewProxy; -// } + @Override + public void release() + { + if (layout != null) { + layout.removeAllViews(); + layout = null; + } + if (centerView != null) { + centerView.removeAllViews(); + centerView = null; + } + super.release(); + proxy = null; + } } From a89b6cdc9a1e9ff195327e94c7fdfd22a731d257 Mon Sep 17 00:00:00 2001 From: Michael Gangolf Date: Wed, 2 Oct 2024 12:53:14 +0200 Subject: [PATCH 3/6] use old tabgroup layout --- .../titanium/ui/BottomNavigationProxy.java | 150 -------------- .../ti/modules/titanium/ui/TabGroupProxy.java | 7 +- .../java/ti/modules/titanium/ui/TabProxy.java | 16 -- .../widget/tabgroup/TiUIAbstractTabGroup.java | 2 +- .../{ => tabgroup}/TiUIBottomNavigation.java | 189 ++++++++++++++---- 5 files changed, 152 insertions(+), 212 deletions(-) delete mode 100644 android/modules/ui/src/java/ti/modules/titanium/ui/BottomNavigationProxy.java rename android/modules/ui/src/java/ti/modules/titanium/ui/widget/{ => tabgroup}/TiUIBottomNavigation.java (54%) diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/BottomNavigationProxy.java b/android/modules/ui/src/java/ti/modules/titanium/ui/BottomNavigationProxy.java deleted file mode 100644 index 3a8bc1480ec..00000000000 --- a/android/modules/ui/src/java/ti/modules/titanium/ui/BottomNavigationProxy.java +++ /dev/null @@ -1,150 +0,0 @@ -package ti.modules.titanium.ui; - -import android.app.Activity; -import android.content.Intent; -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; - -import org.appcelerator.kroll.KrollDict; -import org.appcelerator.kroll.annotations.Kroll; -import org.appcelerator.kroll.common.Log; -import org.appcelerator.titanium.TiActivity; -import org.appcelerator.titanium.TiActivityWindow; -import org.appcelerator.titanium.TiActivityWindows; -import org.appcelerator.titanium.TiApplication; -import org.appcelerator.titanium.TiBaseActivity; -import org.appcelerator.titanium.TiC; -import org.appcelerator.titanium.TiRootActivity; -import org.appcelerator.titanium.proxy.TiWindowProxy; -import org.appcelerator.titanium.util.TiConvert; -import org.appcelerator.titanium.util.TiRHelper; - -import java.lang.ref.WeakReference; - -import ti.modules.titanium.ui.widget.TiUIBottomNavigation; - -@Kroll.proxy(creatableInModule = UIModule.class, propertyAccessors = {}) -public class BottomNavigationProxy extends TiWindowProxy implements TiActivityWindow -{ - private static final String TAG = "BottomNavigation"; - Object tabs; - private TiUIBottomNavigation bottomNavigation; - private WeakReference bottomNavigationActivity = new WeakReference<>(null); - - @Override - protected void handleOpen(KrollDict options) - { - Activity topActivity = TiApplication.getAppCurrentActivity(); - // Don't open if app is closing or closed - if (topActivity == null || topActivity.isFinishing()) { - return; - } - - if (getProperty(TiC.PROPERTY_THEME) != null) { - try { - String themeName = getProperty(TiC.PROPERTY_THEME).toString(); - int theme = TiRHelper.getResource("style." - + themeName.replaceAll("[^A-Za-z0-9_]", "_")); - topActivity.setTheme(theme); - topActivity.getApplicationContext().setTheme(theme); - } catch (Exception e) { - } - } - Intent intent = new Intent(topActivity, TiActivity.class); - fillIntent(topActivity, intent); - - int windowId = TiActivityWindows.addWindow(this); - intent.putExtra(TiC.INTENT_PROPERTY_WINDOW_ID, windowId); - - boolean animated = TiConvert.toBoolean(options, TiC.PROPERTY_ANIMATED, true); - if (!animated) { - intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); - topActivity.startActivity(intent); - topActivity.overridePendingTransition(0, 0); - } else if (options.containsKey(TiC.PROPERTY_ACTIVITY_ENTER_ANIMATION) - || options.containsKey(TiC.PROPERTY_ACTIVITY_EXIT_ANIMATION)) { - topActivity.startActivity(intent); - int enterAnimation = TiConvert.toInt(options.get(TiC.PROPERTY_ACTIVITY_ENTER_ANIMATION), 0); - int exitAnimation = TiConvert.toInt(options.get(TiC.PROPERTY_ACTIVITY_EXIT_ANIMATION), 0); - topActivity.overridePendingTransition(enterAnimation, exitAnimation); - } else { - topActivity.startActivity(intent); - if (topActivity instanceof TiRootActivity) { - // A fade-in transition from root splash screen to first window looks better than a slide-up. - // Also works-around issue where splash in mid-transition might do a 2nd transition on cold start. - topActivity.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); - } - } - } - - @Override - protected void handleClose(@NonNull KrollDict options) - { - // Remove this TabGroup proxy from the active/open collection. - // Note: If the activity's onCreate() can't find this proxy, then it'll automatically destroy itself. - // This is needed in case the proxy's close() method was called before the activity was created. - TiActivityWindows.removeWindow(this); - bottomNavigationActivity.clear(); - bottomNavigation.release(); - // Release views/resources. - modelListener = null; - releaseViews(); - view = null; - - // Destroy this proxy's activity. - AppCompatActivity activity = (bottomNavigationActivity != null) ? bottomNavigationActivity.get() : null; - bottomNavigationActivity = null; - if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) { - activity.finish(); - } - - // NOTE: this does not directly fire the close event, but is fired by closeFromActivity() - } - - @Override - protected Activity getWindowActivity() - { - return (bottomNavigationActivity != null) ? bottomNavigationActivity.get() : null; - } - - @Override - public void windowCreated(TiBaseActivity activity, Bundle savedInstanceState) - { - bottomNavigationActivity = new WeakReference<>(activity); - activity.setWindowProxy(this); - activity.setLayoutProxy(this); - setActivity(activity); - - view = new TiUIBottomNavigation(this, activity); - - setModelListener(view); - } - - @Override - public void handleCreationDict(KrollDict options) - { - super.handleCreationDict(options); - // Support setting orientation modes at creation. - Object orientationModes = options.get(TiC.PROPERTY_ORIENTATION_MODES); - if (orientationModes instanceof Object[]) { - try { - int[] modes = TiConvert.toIntArray((Object[]) orientationModes); - setOrientationModes(modes); - - } catch (ClassCastException e) { - Log.e(TAG, "Invalid orientationMode array. Must only contain orientation mode constants."); - } - } - - if (options.containsKeyAndNotNull(TiC.PROPERTY_TABS)) { - tabs = options.get(TiC.PROPERTY_TABS); - TiUIBottomNavigation tabGroup = (TiUIBottomNavigation) view; - if (tabGroup != null) { - tabGroup.setTabs(tabs); - } - } - } - -} diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/TabGroupProxy.java b/android/modules/ui/src/java/ti/modules/titanium/ui/TabGroupProxy.java index 0c76925eb2e..a35ab1e738f 100644 --- a/android/modules/ui/src/java/ti/modules/titanium/ui/TabGroupProxy.java +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/TabGroupProxy.java @@ -39,6 +39,7 @@ import ti.modules.titanium.ui.android.AndroidModule; import ti.modules.titanium.ui.widget.tabgroup.TiUIAbstractTabGroup; +import ti.modules.titanium.ui.widget.tabgroup.TiUIBottomNavigation; import ti.modules.titanium.ui.widget.tabgroup.TiUIBottomNavigationTabGroup; import ti.modules.titanium.ui.widget.tabgroup.TiUITabLayoutTabGroup; @@ -367,7 +368,11 @@ public void windowCreated(TiBaseActivity activity, Bundle savedInstanceState) ((TiUITabLayoutTabGroup) view).setTabMode((Integer) getProperty(TiC.PROPERTY_TAB_MODE)); } } else { - view = new TiUIBottomNavigationTabGroup(this, activity); + if (TiConvert.toBoolean(getProperty("newLayout"), false)) { + view = new TiUIBottomNavigation(this, activity); + } else { + view = new TiUIBottomNavigationTabGroup(this, activity); + } } // If we have set a title before the creation of the native view, set it now. if (this.tabGroupTitle != null) { diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/TabProxy.java b/android/modules/ui/src/java/ti/modules/titanium/ui/TabProxy.java index 29ec8cc7bbd..09ea35d055f 100644 --- a/android/modules/ui/src/java/ti/modules/titanium/ui/TabProxy.java +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/TabProxy.java @@ -15,7 +15,6 @@ import org.appcelerator.titanium.util.TiUIHelper; import org.appcelerator.titanium.view.TiUIView; -import ti.modules.titanium.ui.widget.TiUIBottomNavigation; import ti.modules.titanium.ui.widget.tabgroup.TiUIAbstractTabGroup; import android.app.Activity; @@ -40,11 +39,9 @@ public class TabProxy extends TiViewProxy private static final String TAG = "TabProxy"; private TabGroupProxy tabGroupProxy; - private TiUIBottomNavigation navBar; private TiWindowProxy window; private boolean windowOpened = false; private int windowId; - private int navBarId; public TabProxy() { @@ -144,12 +141,6 @@ public void setTabGroup(TabGroupProxy tabGroupProxy) } } - public void setNavBar(TiUIBottomNavigation bottomNav, int idx) - { - this.navBar = bottomNav; - this.navBarId = idx; - } - public void setWindowId(int id) { windowId = id; @@ -238,13 +229,6 @@ public void onPropertyChanged(String name, Object value) { super.onPropertyChanged(name, value); - if (navBar != null) { - if (name.equals(TiC.PROPERTY_BADGE)) { - navBar.updateBadge(this.navBarId); - } - return; - } - // Fetch the TabGroup's view. If currently null, then we have to wait for TabGroup activity to be created. TiUIView view = (this.tabGroupProxy != null) ? this.tabGroupProxy.peekView() : null; if (!(view instanceof TiUIAbstractTabGroup)) { diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIAbstractTabGroup.java b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIAbstractTabGroup.java index 57d27a4ebab..900d28365a1 100644 --- a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIAbstractTabGroup.java +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIAbstractTabGroup.java @@ -454,7 +454,7 @@ public void onPageScrollStateChanged(int i) // Set action bar color. if (proxy != null) { final ActionBar actionBar = ((AppCompatActivity) proxy.getActivity()).getSupportActionBar(); - if (actionBar != null) { + if (actionBar != null && !this.tabs.isEmpty()) { final TiWindowProxy windowProxy = ((TabProxy) this.tabs.get(tabIndex).getProxy()).getWindow(); final KrollDict windowProperties = windowProxy.getProperties(); final KrollDict properties = getProxy().getProperties(); diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/TiUIBottomNavigation.java b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigation.java similarity index 54% rename from android/modules/ui/src/java/ti/modules/titanium/ui/widget/TiUIBottomNavigation.java rename to android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigation.java index 64d76ecce93..e42723eb2ba 100644 --- a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/TiUIBottomNavigation.java +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigation.java @@ -1,5 +1,12 @@ -package ti.modules.titanium.ui.widget; - +/** + * Titanium SDK + * Copyright TiDev, Inc. 04/07/2022-Present. All Rights Reserved. + * Licensed under the terms of the Apache Public License + * Please see the LICENSE included with this distribution for details. + */ +package ti.modules.titanium.ui.widget.tabgroup; + +import android.annotation.SuppressLint; import android.graphics.drawable.Drawable; import android.util.Log; import android.view.LayoutInflater; @@ -23,24 +30,67 @@ import java.util.ArrayList; +import ti.modules.titanium.ui.TabGroupProxy; import ti.modules.titanium.ui.TabProxy; -import ti.modules.titanium.ui.widget.tabgroup.TiUITab; -public class TiUIBottomNavigation extends TiUIView implements BottomNavigationView.OnItemSelectedListener +/** + * TabGroup implementation using BottomNavigationView as a controller. + */ +public class TiUIBottomNavigation extends TiUIAbstractTabGroup implements BottomNavigationView.OnItemSelectedListener { - private static final String TAG = "BottomNavigation"; + static int id_layout = 0; static int id_content = 0; static int id_bottomNavigation = 0; - private final ArrayList mMenuItemsArray = new ArrayList<>(); + private ArrayList mMenuItemsArray = new ArrayList<>(); private RelativeLayout layout = null; private FrameLayout centerView; private BottomNavigationView bottomNavigation; private Object[] tabsArray; - public TiUIBottomNavigation(TiViewProxy proxy, TiBaseActivity activity) + public TiUIBottomNavigation(TabGroupProxy proxy, TiBaseActivity activity) + { + super(proxy, activity); + if (proxy.getProperty(TiC.PROPERTY_THEME) != null) { + try { + String themeName = proxy.getProperty(TiC.PROPERTY_THEME).toString(); + int theme = TiRHelper.getResource("style." + + themeName.replaceAll("[^A-Za-z0-9_]", "_")); + activity.setTheme(theme); + activity.getApplicationContext().setTheme(theme); + } catch (Exception e) { + } + } + } + + // Overriding addTab method to provide a proper guard for trying to add more tabs than the limit + // for BottomNavigationView class. + @Override + public void addTab(TabProxy tabProxy) + { + + } + + public void setTabs(Object tabs) + { + if (tabs instanceof Object[] objArray) { + tabsArray = objArray; + for (Object tabView : tabsArray) { + if (tabView instanceof TabProxy tp) { + MenuItem menuItem = bottomNavigation.getMenu().add(0, mMenuItemsArray.size(), 0, ""); + menuItem.setTitle(tp.getProperty(TiC.PROPERTY_TITLE).toString()); + Drawable drawable = TiUIHelper.getResourceDrawable(tp.getProperty(TiC.PROPERTY_ICON)); + menuItem.setIcon(drawable); + mMenuItemsArray.add(menuItem); + } + } + } + } + + @Override + public void addViews(TiBaseActivity activity) { - super(proxy); + mMenuItemsArray = new ArrayList<>(); try { id_layout = TiRHelper.getResource("layout.titanium_ui_bottom_navigation"); id_content = TiRHelper.getResource("id.bottomNavBar_content"); @@ -58,13 +108,70 @@ public TiUIBottomNavigation(TiViewProxy proxy, TiBaseActivity activity) setTabs(proxy.getProperty(TiC.PROPERTY_TABS)); selectTab(0); } - } catch (Exception ex) { Log.e(TAG, "XML resources could not be found!!!" + ex.getMessage()); } + } + + /** + * Handle the removing of the controller from the UI layout when tab navigation is disabled. + * + * @param disable + */ + @Override + public void disableTabNavigation(boolean disable) + { + super.disableTabNavigation(disable); + } + + @Override + public void addTabItemInController(TiViewProxy tabProxy) + { + + } + + /** + * Remove an item from the BottomNavigationView for a specific index. + * + * @param position the position of the removed item. + */ + @Override + public void removeTabItemFromController(int position) + { } + /** + * Select an item from the BottomNavigationView with a specific position. + * + * @param position the position of the item to be selected. + */ + @Override + public void selectTabItemInController(int position) + { + + } + + @Override + public void setBackgroundColor(int colorInt) + { + + } + + @Override + public void updateTabBackgroundDrawable(int index) + { + + } + + @Override + public void updateTabTitle(int index) + { + + } + + @SuppressLint("RestrictedApi") + @Override public void updateBadge(int index) { if ((index < 0) || (index >= tabsArray.length)) { @@ -91,9 +198,36 @@ public void updateBadge(int index) } } - private void selectTab(int id) + @Override + public void updateBadgeColor(int index) + { + + } + + @Override + public void updateTabTitleColor(int index) + { + + } + + @Override + public void updateTabIcon(int index) + { + + } + + @Override + public String getTabTitle(int index) + { + return ""; + } + + @Override + public void selectTab(int tabIndex) { - TabProxy tp = ((TabProxy) tabsArray[id]); + super.selectTab(tabIndex); + + TabProxy tp = ((TabProxy) tabsArray[tabIndex]); if (tp != null) { TiUITab abstractTab = new TiUITab(tp); @@ -102,7 +236,6 @@ private void selectTab(int id) if (view != null) { centerView.addView(view.getOuterView()); } - } } @@ -113,36 +246,4 @@ public boolean onNavigationItemSelected(@NonNull MenuItem item) selectTab(item.getItemId()); return true; } - - public void setTabs(Object tabs) - { - if (tabs instanceof Object[] objArray) { - tabsArray = objArray; - for (Object tabView : tabsArray) { - if (tabView instanceof TabProxy tp) { - MenuItem menuItem = bottomNavigation.getMenu().add(0, this.mMenuItemsArray.size(), 0, ""); - tp.setNavBar(this, menuItem.getItemId()); - menuItem.setTitle(tp.getProperty(TiC.PROPERTY_TITLE).toString()); - Drawable drawable = TiUIHelper.getResourceDrawable(tp.getProperty(TiC.PROPERTY_ICON)); - menuItem.setIcon(drawable); - this.mMenuItemsArray.add(menuItem); - } - } - } - } - - @Override - public void release() - { - if (layout != null) { - layout.removeAllViews(); - layout = null; - } - if (centerView != null) { - centerView.removeAllViews(); - centerView = null; - } - super.release(); - proxy = null; - } } From 4978b224652b08f362a74aade692e45667395cff Mon Sep 17 00:00:00 2001 From: Michael Gangolf Date: Wed, 2 Oct 2024 13:14:08 +0200 Subject: [PATCH 4/6] theme --- .../java/ti/modules/titanium/ui/TabGroupProxy.java | 14 ++++++++++++++ .../ui/widget/tabgroup/TiUIBottomNavigation.java | 10 ---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/TabGroupProxy.java b/android/modules/ui/src/java/ti/modules/titanium/ui/TabGroupProxy.java index a35ab1e738f..d0327211441 100644 --- a/android/modules/ui/src/java/ti/modules/titanium/ui/TabGroupProxy.java +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/TabGroupProxy.java @@ -324,6 +324,20 @@ protected void handleOpen(KrollDict options) if (topActivity == null || topActivity.isFinishing()) { return; } + + // set theme for XML layout + if (((Integer) getProperty(TiC.PROPERTY_STYLE)) == AndroidModule.TABS_STYLE_BOTTOM_NAVIGATION + && getProperty(TiC.PROPERTY_THEME) != null) { + try { + String themeName = getProperty(TiC.PROPERTY_THEME).toString(); + int theme = TiRHelper.getResource("style." + + themeName.replaceAll("[^A-Za-z0-9_]", "_")); + topActivity.setTheme(theme); + topActivity.getApplicationContext().setTheme(theme); + } catch (Exception e) { + } + } + Intent intent = new Intent(topActivity, TiActivity.class); fillIntent(topActivity, intent); diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigation.java b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigation.java index e42723eb2ba..359bcb4090b 100644 --- a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigation.java +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigation.java @@ -51,16 +51,6 @@ public class TiUIBottomNavigation extends TiUIAbstractTabGroup implements Bottom public TiUIBottomNavigation(TabGroupProxy proxy, TiBaseActivity activity) { super(proxy, activity); - if (proxy.getProperty(TiC.PROPERTY_THEME) != null) { - try { - String themeName = proxy.getProperty(TiC.PROPERTY_THEME).toString(); - int theme = TiRHelper.getResource("style." - + themeName.replaceAll("[^A-Za-z0-9_]", "_")); - activity.setTheme(theme); - activity.getApplicationContext().setTheme(theme); - } catch (Exception e) { - } - } } // Overriding addTab method to provide a proper guard for trying to add more tabs than the limit From 9f3b4c24beab54f7deb38501e7ec08eadfad2560 Mon Sep 17 00:00:00 2001 From: Michael Gangolf Date: Wed, 2 Oct 2024 23:20:12 +0200 Subject: [PATCH 5/6] more features --- .../ti/modules/titanium/ui/TabGroupProxy.java | 6 +- .../widget/tabgroup/TiUIAbstractTabGroup.java | 12 ++ .../widget/tabgroup/TiUIBottomNavigation.java | 169 +++++++++++++++++- .../TiUIBottomNavigationTabGroup.java | 6 + .../tabgroup/TiUITabLayoutTabGroup.java | 6 + 5 files changed, 194 insertions(+), 5 deletions(-) diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/TabGroupProxy.java b/android/modules/ui/src/java/ti/modules/titanium/ui/TabGroupProxy.java index d0327211441..1dee4f9dbbc 100644 --- a/android/modules/ui/src/java/ti/modules/titanium/ui/TabGroupProxy.java +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/TabGroupProxy.java @@ -50,7 +50,8 @@ TiC.PROPERTY_SWIPEABLE, TiC.PROPERTY_AUTO_TAB_TITLE, TiC.PROPERTY_EXIT_ON_CLOSE, - TiC.PROPERTY_SMOOTH_SCROLL_ON_TAB_CLICK + TiC.PROPERTY_SMOOTH_SCROLL_ON_TAB_CLICK, + TiC.PROPERTY_INDICATOR_COLOR }) public class TabGroupProxy extends TiWindowProxy implements TiActivityWindow { @@ -326,7 +327,8 @@ protected void handleOpen(KrollDict options) } // set theme for XML layout - if (((Integer) getProperty(TiC.PROPERTY_STYLE)) == AndroidModule.TABS_STYLE_BOTTOM_NAVIGATION + if (hasProperty(TiC.PROPERTY_STYLE) + && ((Integer) getProperty(TiC.PROPERTY_STYLE)) == AndroidModule.TABS_STYLE_BOTTOM_NAVIGATION && getProperty(TiC.PROPERTY_THEME) != null) { try { String themeName = getProperty(TiC.PROPERTY_THEME).toString(); diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIAbstractTabGroup.java b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIAbstractTabGroup.java index 900d28365a1..b1f33741685 100644 --- a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIAbstractTabGroup.java +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIAbstractTabGroup.java @@ -115,6 +115,13 @@ public abstract class TiUIAbstractTabGroup extends TiUIView */ public abstract void updateTabBackgroundDrawable(int index); + /** + * Material 3 active indicator color + * + * @param color color + */ + public abstract void updateActiveIndicatorColor(int color); + /** * Update the tab's title to the proper text. * @@ -495,6 +502,9 @@ public void processProperties(KrollDict d) } else { setBackgroundColor(getDefaultBackgroundColor()); } + if (d.containsKeyAndNotNull(TiC.PROPERTY_INDICATOR_COLOR)) { + updateActiveIndicatorColor(TiConvert.toColor(d, TiC.PROPERTY_INDICATOR_COLOR, proxy.getActivity())); + } super.processProperties(d); } @@ -516,6 +526,8 @@ public void propertyChanged(String key, Object oldValue, Object newValue, KrollP for (TiUITab tabView : tabs) { updateTabBackgroundDrawable(tabs.indexOf(tabView)); } + } else if (key.equals(TiC.PROPERTY_INDICATOR_COLOR)) { + updateActiveIndicatorColor(TiColorHelper.parseColor(newValue.toString(), proxy.getActivity())); } else { super.propertyChanged(key, oldValue, newValue, proxy); } diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigation.java b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigation.java index 359bcb4090b..c76b1562ae1 100644 --- a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigation.java +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigation.java @@ -7,17 +7,27 @@ package ti.modules.titanium.ui.widget.tabgroup; import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.res.ColorStateList; import android.graphics.drawable.Drawable; +import android.graphics.drawable.RippleDrawable; +import android.os.Build; import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; +import android.view.View; +import android.view.Window; import android.widget.FrameLayout; import android.widget.RelativeLayout; import androidx.annotation.NonNull; +import androidx.core.graphics.ColorUtils; import com.google.android.material.badge.BadgeDrawable; +import com.google.android.material.bottomnavigation.BottomNavigationItemView; +import com.google.android.material.bottomnavigation.BottomNavigationMenuView; import com.google.android.material.bottomnavigation.BottomNavigationView; +import com.google.android.material.shape.MaterialShapeDrawable; import org.appcelerator.titanium.TiApplication; import org.appcelerator.titanium.TiBaseActivity; @@ -42,6 +52,7 @@ public class TiUIBottomNavigation extends TiUIAbstractTabGroup implements Bottom static int id_layout = 0; static int id_content = 0; static int id_bottomNavigation = 0; + private final int currentlySelectedIndex = -1; private ArrayList mMenuItemsArray = new ArrayList<>(); private RelativeLayout layout = null; private FrameLayout centerView; @@ -67,7 +78,7 @@ public void setTabs(Object tabs) tabsArray = objArray; for (Object tabView : tabsArray) { if (tabView instanceof TabProxy tp) { - MenuItem menuItem = bottomNavigation.getMenu().add(0, mMenuItemsArray.size(), 0, ""); + MenuItem menuItem = bottomNavigation.getMenu().add(0, mMenuItemsArray.size(), 0, ""); menuItem.setTitle(tp.getProperty(TiC.PROPERTY_TITLE).toString()); Drawable drawable = TiUIHelper.getResourceDrawable(tp.getProperty(TiC.PROPERTY_ICON)); menuItem.setIcon(drawable); @@ -98,6 +109,7 @@ public void addViews(TiBaseActivity activity) setTabs(proxy.getProperty(TiC.PROPERTY_TABS)); selectTab(0); } + } catch (Exception ex) { Log.e(TAG, "XML resources could not be found!!!" + ex.getMessage()); } @@ -145,19 +157,72 @@ public void selectTabItemInController(int position) @Override public void setBackgroundColor(int colorInt) { + // Update tab bar's background color. + Drawable drawable = bottomNavigation.getBackground(); + if (drawable instanceof MaterialShapeDrawable shapeDrawable) { + shapeDrawable.setFillColor(ColorStateList.valueOf(colorInt)); + shapeDrawable.setElevation(0); // Drawable will tint the fill color if elevation is non-zero. + } else { + bottomNavigation.setBackgroundColor(colorInt); + } + // Apply given color to bottom navigation bar if using a "solid" theme. + if (isUsingSolidTitaniumTheme() && (Build.VERSION.SDK_INT >= 27)) { + Activity activity = (this.proxy != null) ? this.proxy.getActivity() : null; + Window window = (activity != null) ? activity.getWindow() : null; + View decorView = (window != null) ? window.getDecorView() : null; + if ((window != null) && (decorView != null)) { + int uiFlags = decorView.getSystemUiVisibility(); + if (ColorUtils.calculateLuminance(colorInt) > 0.5) { + uiFlags |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + } else { + uiFlags &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + } + decorView.setSystemUiVisibility(uiFlags); + window.setNavigationBarColor(colorInt); + } + } } @Override + @SuppressLint("RestrictedApi") public void updateTabBackgroundDrawable(int index) { - + try { + // BottomNavigationMenuView rebuilds itself after adding a new item, so we need to reset the colors each time. + TiViewProxy tabProxy = tabs.get(index).getProxy(); + boolean hasTouchFeedbackColor = tabProxy.hasPropertyAndNotNull(TiC.PROPERTY_TOUCH_FEEDBACK_COLOR); + if (hasCustomBackground(tabProxy) || hasCustomIconTint(tabProxy) || hasTouchFeedbackColor) { + BottomNavigationMenuView bottomMenuView = + ((BottomNavigationMenuView) this.bottomNavigation.getChildAt(0)); + Drawable drawable = createBackgroundDrawableForState(tabProxy, android.R.attr.state_checked); + int color = getActiveColor(tabProxy); + if (hasTouchFeedbackColor) { + color = TiConvert.toColor(tabProxy.getProperty(TiC.PROPERTY_TOUCH_FEEDBACK_COLOR), + tabProxy.getActivity()); + } + drawable = new RippleDrawable(createRippleColorStateListFrom(color), drawable, null); + bottomMenuView.getChildAt(index).setBackground(drawable); + } + } catch (Exception e) { + org.appcelerator.kroll.common.Log.w(TAG, WARNING_LAYOUT_MESSAGE); + } } @Override public void updateTabTitle(int index) { + if ((index < 0) || (index >= this.tabs.size())) { + return; + } + + TiViewProxy tabProxy = this.tabs.get(index).getProxy(); + if (tabProxy == null) { + return; + } + String title = TiConvert.toString(tabProxy.getProperty(TiC.PROPERTY_TITLE)); + this.bottomNavigation.getMenu().getItem(index).setTitle(title); } @SuppressLint("RestrictedApi") @@ -191,25 +256,122 @@ public void updateBadge(int index) @Override public void updateBadgeColor(int index) { + if ((index < 0) || (index >= this.tabs.size())) { + return; + } + + TiViewProxy tabProxy = this.tabs.get(index).getProxy(); + if (tabProxy == null) { + return; + } + // TODO: reset to default value when property is null + if (tabProxy.hasPropertyAndNotNull(TiC.PROPERTY_BADGE_COLOR)) { + org.appcelerator.kroll.common.Log.w(TAG, "badgeColor is deprecated. Use badgeBackgroundColor instead."); + int menuItemId = this.bottomNavigation.getMenu().getItem(index).getItemId(); + BadgeDrawable badgeDrawable = this.bottomNavigation.getOrCreateBadge(menuItemId); + badgeDrawable.setBackgroundColor( + TiConvert.toColor(tabProxy.getProperty(TiC.PROPERTY_BADGE_COLOR), tabProxy.getActivity())); + } + if (tabProxy.hasPropertyAndNotNull(TiC.PROPERTY_BADGE_BACKGROUND_COLOR)) { + int menuItemId = this.bottomNavigation.getMenu().getItem(index).getItemId(); + BadgeDrawable badgeDrawable = this.bottomNavigation.getOrCreateBadge(menuItemId); + badgeDrawable.setBackgroundColor( + TiConvert.toColor(tabProxy.getProperty(TiC.PROPERTY_BADGE_BACKGROUND_COLOR), tabProxy.getActivity())); + } + if (tabProxy.hasPropertyAndNotNull(TiC.PROPERTY_BADGE_TEXT_COLOR)) { + int menuItemId = this.bottomNavigation.getMenu().getItem(index).getItemId(); + BadgeDrawable badgeDrawable = this.bottomNavigation.getOrCreateBadge(menuItemId); + badgeDrawable.setBadgeTextColor( + TiConvert.toColor(tabProxy.getProperty(TiC.PROPERTY_BADGE_TEXT_COLOR), tabProxy.getActivity())); + } } @Override + @SuppressLint("RestrictedApi") public void updateTabTitleColor(int index) { + try { + // BottomNavigationMenuView rebuilds itself after adding a new item, so we need to reset the colors each time. + TiViewProxy tabProxy = tabs.get(index).getProxy(); + if (hasCustomTextColor(tabProxy)) { + // Set the TextView textColor. + BottomNavigationMenuView bottomMenuView = + ((BottomNavigationMenuView) this.bottomNavigation.getChildAt(0)); + ((BottomNavigationItemView) bottomMenuView.getChildAt(index)) + .setTextColor(textColorStateList(tabProxy, android.R.attr.state_checked)); + } + } catch (Exception e) { + org.appcelerator.kroll.common.Log.w(TAG, WARNING_LAYOUT_MESSAGE); + } + } + @SuppressLint("RestrictedApi") + public void updateActiveIndicatorColor(int color) + { + try { + // BottomNavigationMenuView rebuilds itself after adding a new item, so we need to reset the colors each time. + + int[][] states = new int[][] { + new int[] { android.R.attr.state_enabled }, // enabled + new int[] { -android.R.attr.state_enabled }, // disabled + new int[] { -android.R.attr.state_checked }, // unchecked + new int[] { android.R.attr.state_pressed } // pressed + }; + + int[] colors = new int[] { + color, + color, + color, + color + }; + + ColorStateList myList = new ColorStateList(states, colors); + + bottomNavigation.setItemActiveIndicatorColor(myList); + } catch (Exception e) { + org.appcelerator.kroll.common.Log.w(TAG, WARNING_LAYOUT_MESSAGE); + } } @Override public void updateTabIcon(int index) { + if ((index < 0) || (index >= this.tabs.size())) { + return; + } + + TiViewProxy tabProxy = this.tabs.get(index).getProxy(); + if (tabProxy == null) { + return; + } + final Drawable drawable = TiUIHelper.getResourceDrawable(tabProxy.getProperty(TiC.PROPERTY_ICON)); + this.bottomNavigation.getMenu().getItem(index).setIcon(drawable); + updateIconTint(); + } + + private void updateIconTint() + { + for (int i = 0; i < this.tabs.size(); i++) { + final TiViewProxy tabProxy = this.tabs.get(i).getProxy(); + if (hasCustomIconTint(tabProxy)) { + final boolean selected = i == currentlySelectedIndex; + Drawable drawable = this.bottomNavigation.getMenu().getItem(i).getIcon(); + drawable = updateIconTint(tabProxy, drawable, selected); + this.bottomNavigation.getMenu().getItem(i).setIcon(drawable); + } + } } @Override public String getTabTitle(int index) { - return ""; + // Validate index. + if (index < 0 || index > tabs.size() - 1) { + return null; + } + return this.bottomNavigation.getMenu().getItem(index).getTitle().toString(); } @Override @@ -234,6 +396,7 @@ public boolean onNavigationItemSelected(@NonNull MenuItem item) { item.setChecked(true); selectTab(item.getItemId()); + ((TabGroupProxy) getProxy()).onTabSelected(item.getItemId()); return true; } } diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigationTabGroup.java b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigationTabGroup.java index 1ead9f66615..6cb337083d3 100644 --- a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigationTabGroup.java +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigationTabGroup.java @@ -362,6 +362,12 @@ public void updateTabBackgroundDrawable(int index) } } + @Override + public void updateActiveIndicatorColor(int color) + { + + } + @Override public void updateTabTitle(int index) { diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUITabLayoutTabGroup.java b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUITabLayoutTabGroup.java index 97d81da263e..5039a67ee36 100644 --- a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUITabLayoutTabGroup.java +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUITabLayoutTabGroup.java @@ -235,6 +235,12 @@ public void updateTabBackgroundDrawable(int index) this.mTabLayout.setBackground(backgroundDrawable); } + @Override + public void updateActiveIndicatorColor(int color) + { + + } + @Override public void updateTabTitle(int index) { From c1294ecb2839a049c100c8fa707d73a2ff243204 Mon Sep 17 00:00:00 2001 From: Michael Gangolf Date: Sat, 5 Oct 2024 19:24:26 +0200 Subject: [PATCH 6/6] more colors --- .../widget/tabgroup/TiUIAbstractTabGroup.java | 1 - .../widget/tabgroup/TiUIBottomNavigation.java | 79 +++++++++++++------ 2 files changed, 57 insertions(+), 23 deletions(-) diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIAbstractTabGroup.java b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIAbstractTabGroup.java index b1f33741685..fb4afb02cb3 100644 --- a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIAbstractTabGroup.java +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIAbstractTabGroup.java @@ -562,7 +562,6 @@ public Drawable updateIconTint(TiViewProxy tabProxy, Drawable drawable, boolean if (drawable == null) { return null; } - // Clone existing drawable so color filter applies correctly. drawable = drawable.getConstantState().newDrawable(); diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigation.java b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigation.java index c76b1562ae1..9407681bfe0 100644 --- a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigation.java +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigation.java @@ -24,9 +24,9 @@ import androidx.core.graphics.ColorUtils; import com.google.android.material.badge.BadgeDrawable; -import com.google.android.material.bottomnavigation.BottomNavigationItemView; import com.google.android.material.bottomnavigation.BottomNavigationMenuView; import com.google.android.material.bottomnavigation.BottomNavigationView; +import com.google.android.material.navigation.NavigationBarView; import com.google.android.material.shape.MaterialShapeDrawable; import org.appcelerator.titanium.TiApplication; @@ -52,12 +52,13 @@ public class TiUIBottomNavigation extends TiUIAbstractTabGroup implements Bottom static int id_layout = 0; static int id_content = 0; static int id_bottomNavigation = 0; - private final int currentlySelectedIndex = -1; + private int currentlySelectedIndex = -1; private ArrayList mMenuItemsArray = new ArrayList<>(); private RelativeLayout layout = null; private FrameLayout centerView; private BottomNavigationView bottomNavigation; private Object[] tabsArray; + protected final static String TAG = "TiUIBottomNavigation"; public TiUIBottomNavigation(TabGroupProxy proxy, TiBaseActivity activity) { @@ -69,7 +70,15 @@ public TiUIBottomNavigation(TabGroupProxy proxy, TiBaseActivity activity) @Override public void addTab(TabProxy tabProxy) { - + if (this.bottomNavigation == null) { + return; + } + final int MAX_TABS = this.bottomNavigation.getMaxItemCount(); + if (this.tabs.size() < MAX_TABS) { + super.addTab(tabProxy); + } else { + Log.w(TAG, "Bottom style TabGroup cannot have more than " + MAX_TABS + " tabs."); + } } public void setTabs(Object tabs) @@ -83,9 +92,12 @@ public void setTabs(Object tabs) Drawable drawable = TiUIHelper.getResourceDrawable(tp.getProperty(TiC.PROPERTY_ICON)); menuItem.setIcon(drawable); mMenuItemsArray.add(menuItem); + int index = this.mMenuItemsArray.size() - 1; + updateDrawablesAfterNewItem(index); } } } + } @Override @@ -129,7 +141,19 @@ public void disableTabNavigation(boolean disable) @Override public void addTabItemInController(TiViewProxy tabProxy) { - + final int shiftMode = proxy.getProperties().optInt(TiC.PROPERTY_SHIFT_MODE, 1); + switch (shiftMode) { + case 0: + this.bottomNavigation.setLabelVisibilityMode(NavigationBarView.LABEL_VISIBILITY_LABELED); + break; + case 1: + this.bottomNavigation.setLabelVisibilityMode(NavigationBarView.LABEL_VISIBILITY_AUTO); + break; + case 2: + // NOTE: Undocumented for now, will create new property that has parity with iOS. + this.bottomNavigation.setLabelVisibilityMode(NavigationBarView.LABEL_VISIBILITY_UNLABELED); + break; + } } /** @@ -140,7 +164,11 @@ public void addTabItemInController(TiViewProxy tabProxy) @Override public void removeTabItemFromController(int position) { - + this.bottomNavigation.getMenu().clear(); + this.mMenuItemsArray.clear(); + for (TiUITab tabView : tabs) { + addTabItemInController(tabView.getProxy()); + } } /** @@ -154,6 +182,16 @@ public void selectTabItemInController(int position) } + private void updateDrawablesAfterNewItem(int index) + { + updateTabTitle(index); + updateTabIcon(index); + updateBadge(index); + updateBadgeColor(index); + updateTabTitleColor(index); + updateTabBackgroundDrawable(index); + } + @Override public void setBackgroundColor(int colorInt) { @@ -190,7 +228,7 @@ public void updateTabBackgroundDrawable(int index) { try { // BottomNavigationMenuView rebuilds itself after adding a new item, so we need to reset the colors each time. - TiViewProxy tabProxy = tabs.get(index).getProxy(); + TiViewProxy tabProxy = ((TabProxy) tabsArray[index]); boolean hasTouchFeedbackColor = tabProxy.hasPropertyAndNotNull(TiC.PROPERTY_TOUCH_FEEDBACK_COLOR); if (hasCustomBackground(tabProxy) || hasCustomIconTint(tabProxy) || hasTouchFeedbackColor) { BottomNavigationMenuView bottomMenuView = @@ -205,7 +243,7 @@ public void updateTabBackgroundDrawable(int index) bottomMenuView.getChildAt(index).setBackground(drawable); } } catch (Exception e) { - org.appcelerator.kroll.common.Log.w(TAG, WARNING_LAYOUT_MESSAGE); + Log.w(TAG, WARNING_LAYOUT_MESSAGE); } } @@ -216,7 +254,7 @@ public void updateTabTitle(int index) return; } - TiViewProxy tabProxy = this.tabs.get(index).getProxy(); + TiViewProxy tabProxy = ((TabProxy) tabsArray[index]); if (tabProxy == null) { return; } @@ -260,14 +298,14 @@ public void updateBadgeColor(int index) return; } - TiViewProxy tabProxy = this.tabs.get(index).getProxy(); + TiViewProxy tabProxy = ((TabProxy) tabsArray[index]); if (tabProxy == null) { return; } // TODO: reset to default value when property is null if (tabProxy.hasPropertyAndNotNull(TiC.PROPERTY_BADGE_COLOR)) { - org.appcelerator.kroll.common.Log.w(TAG, "badgeColor is deprecated. Use badgeBackgroundColor instead."); + Log.w(TAG, "badgeColor is deprecated. Use badgeBackgroundColor instead."); int menuItemId = this.bottomNavigation.getMenu().getItem(index).getItemId(); BadgeDrawable badgeDrawable = this.bottomNavigation.getOrCreateBadge(menuItemId); badgeDrawable.setBackgroundColor( @@ -292,17 +330,12 @@ public void updateBadgeColor(int index) public void updateTabTitleColor(int index) { try { - // BottomNavigationMenuView rebuilds itself after adding a new item, so we need to reset the colors each time. - TiViewProxy tabProxy = tabs.get(index).getProxy(); + TiViewProxy tabProxy = ((TabProxy) tabsArray[index]); if (hasCustomTextColor(tabProxy)) { - // Set the TextView textColor. - BottomNavigationMenuView bottomMenuView = - ((BottomNavigationMenuView) this.bottomNavigation.getChildAt(0)); - ((BottomNavigationItemView) bottomMenuView.getChildAt(index)) - .setTextColor(textColorStateList(tabProxy, android.R.attr.state_checked)); + this.bottomNavigation.setItemTextColor(textColorStateList(tabProxy, android.R.attr.state_checked)); } } catch (Exception e) { - org.appcelerator.kroll.common.Log.w(TAG, WARNING_LAYOUT_MESSAGE); + Log.w(TAG, WARNING_LAYOUT_MESSAGE); } } @@ -330,7 +363,7 @@ public void updateActiveIndicatorColor(int color) bottomNavigation.setItemActiveIndicatorColor(myList); } catch (Exception e) { - org.appcelerator.kroll.common.Log.w(TAG, WARNING_LAYOUT_MESSAGE); + Log.w(TAG, WARNING_LAYOUT_MESSAGE); } } @@ -341,7 +374,7 @@ public void updateTabIcon(int index) return; } - TiViewProxy tabProxy = this.tabs.get(index).getProxy(); + TiViewProxy tabProxy = ((TabProxy) tabsArray[index]); if (tabProxy == null) { return; } @@ -353,8 +386,8 @@ public void updateTabIcon(int index) private void updateIconTint() { - for (int i = 0; i < this.tabs.size(); i++) { - final TiViewProxy tabProxy = this.tabs.get(i).getProxy(); + for (int i = 0; i < this.bottomNavigation.getMenu().size(); i++) { + TiViewProxy tabProxy = ((TabProxy) tabsArray[i]); if (hasCustomIconTint(tabProxy)) { final boolean selected = i == currentlySelectedIndex; Drawable drawable = this.bottomNavigation.getMenu().getItem(i).getIcon(); @@ -378,6 +411,7 @@ public String getTabTitle(int index) public void selectTab(int tabIndex) { super.selectTab(tabIndex); + currentlySelectedIndex = tabIndex; TabProxy tp = ((TabProxy) tabsArray[tabIndex]); if (tp != null) { @@ -389,6 +423,7 @@ public void selectTab(int tabIndex) centerView.addView(view.getOuterView()); } } + updateIconTint(); } @Override