diff --git a/android/modules/android/src/java/ti/modules/titanium/android/AndroidModule.java b/android/modules/android/src/java/ti/modules/titanium/android/AndroidModule.java
index fdc5c89431a..991380b9eb2 100644
--- a/android/modules/android/src/java/ti/modules/titanium/android/AndroidModule.java
+++ b/android/modules/android/src/java/ti/modules/titanium/android/AndroidModule.java
@@ -363,6 +363,9 @@ public class AndroidModule extends KrollModule
@Kroll.constant
public static final int FLAG_UPDATE_CURRENT = PendingIntent.FLAG_UPDATE_CURRENT;
+ @Kroll.constant
+ public static final int STATUS_BAR_LIGHT = 8192;
+
@Kroll.constant
public static final int RESULT_OK = Activity.RESULT_OK;
@Kroll.constant
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..9a50ac07b42
--- /dev/null
+++ b/android/modules/ui/res/layout/titanium_ui_bottom_navigation.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
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..faa4abc14e6 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
@@ -6,10 +6,14 @@
*/
package ti.modules.titanium.ui;
+import static ti.modules.titanium.android.AndroidModule.STATUS_BAR_LIGHT;
+
import android.app.Activity;
import android.content.Intent;
+import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
+import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
@@ -29,6 +33,7 @@
import org.appcelerator.titanium.TiRootActivity;
import org.appcelerator.titanium.proxy.ActivityProxy;
import org.appcelerator.titanium.proxy.TiWindowProxy;
+import org.appcelerator.titanium.util.TiColorHelper;
import org.appcelerator.titanium.util.TiConvert;
import org.appcelerator.titanium.util.TiRHelper;
import org.appcelerator.titanium.util.TiUIHelper;
@@ -39,6 +44,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;
@@ -49,7 +55,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
{
@@ -69,6 +76,7 @@ public class TabGroupProxy extends TiWindowProxy implements TiActivityWindow
private Object selectedTab; // NOTE: Can be TabProxy or Number
private String tabGroupTitle = null;
private boolean autoTabTitle = false;
+ private boolean tabEnabled = true;
public TabGroupProxy()
{
@@ -189,6 +197,22 @@ public void setActiveTab(Object tabOrIndex)
}
}
+ @Kroll.setProperty
+ public void setEnabled(Boolean enabled)
+ {
+ tabEnabled = enabled;
+ TiUIAbstractTabGroup tabGroup = (TiUIAbstractTabGroup) view;
+ if (tabGroup != null) {
+ tabGroup.setEnabled(enabled);
+ }
+ }
+
+ @Kroll.getProperty
+ public Boolean getEnabled()
+ {
+ return tabEnabled;
+ }
+
private TabProxy getActiveTabProxy()
{
Object activeTab = getActiveTab();
@@ -279,6 +303,9 @@ public void handleCreationDict(KrollDict options)
if (options.containsKeyAndNotNull(TiC.PROPERTY_ACTIVE_TAB)) {
setActiveTab(options.get(TiC.PROPERTY_ACTIVE_TAB));
}
+ if (options.containsKeyAndNotNull(TiC.PROPERTY_ENABLED)) {
+ setEnabled(options.getBoolean(TiC.PROPERTY_ENABLED));
+ }
}
@Kroll.getProperty
@@ -323,6 +350,21 @@ protected void handleOpen(KrollDict options)
if (topActivity == null || topActivity.isFinishing()) {
return;
}
+
+ // set theme for XML layout
+ 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();
+ 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);
@@ -367,7 +409,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("experimental"), 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) {
@@ -405,6 +451,22 @@ public void windowCreated(TiBaseActivity activity, Bundle savedInstanceState)
// Need to handle the cached activity proxy properties in the JS side.
callPropertySync(PROPERTY_POST_TAB_GROUP_CREATED, null);
+
+ if (getActivity() != null) {
+ if (hasPropertyAndNotNull(TiC.PROPERTY_FLAGS)) {
+ if (TiConvert.toInt(getProperty(TiC.PROPERTY_FLAGS)) == STATUS_BAR_LIGHT
+ && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ getActivity().getWindow().getDecorView()
+ .setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+ }
+ }
+ if (hasPropertyAndNotNull(TiC.PROPERTY_STATUS_BAR_COLOR)) {
+ int colorInt = TiColorHelper.parseColor(
+ TiConvert.toString(getProperty(TiC.PROPERTY_STATUS_BAR_COLOR)),
+ TiApplication.getAppRootOrCurrentActivity());
+ getActivity().getWindow().setStatusBarColor(colorInt);
+ }
+ }
}
@Override
diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/WindowProxy.java b/android/modules/ui/src/java/ti/modules/titanium/ui/WindowProxy.java
index 1782c67e97d..c9690a84647 100644
--- a/android/modules/ui/src/java/ti/modules/titanium/ui/WindowProxy.java
+++ b/android/modules/ui/src/java/ti/modules/titanium/ui/WindowProxy.java
@@ -7,6 +7,8 @@
package ti.modules.titanium.ui;
+import static ti.modules.titanium.android.AndroidModule.STATUS_BAR_LIGHT;
+
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
@@ -14,6 +16,7 @@
import android.graphics.PixelFormat;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.text.Spannable;
@@ -333,6 +336,13 @@ public void windowCreated(TiBaseActivity activity, Bundle savedInstanceState)
win.getDecorView().setSystemUiVisibility(TiConvert.toInt(getProperty(TiC.PROPERTY_UI_FLAGS)));
}
+ if (hasProperty(TiC.PROPERTY_WINDOW_FLAGS)) {
+ if ((TiConvert.toInt(getProperty(TiC.PROPERTY_WINDOW_FLAGS)) & STATUS_BAR_LIGHT) != 0
+ && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ win.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+ }
+ }
+
// Handle titleAttributes property.
if (hasProperty(TiC.PROPERTY_TITLE_ATTRIBUTES)) {
KrollDict innerAttributes = getProperties().getKrollDict(TiC.PROPERTY_TITLE_ATTRIBUTES);
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..883daac91ef 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
@@ -40,6 +40,7 @@
import org.appcelerator.titanium.proxy.TiWindowProxy;
import org.appcelerator.titanium.util.TiColorHelper;
import org.appcelerator.titanium.util.TiConvert;
+import org.appcelerator.titanium.util.TiIconDrawable;
import org.appcelerator.titanium.util.TiUIHelper;
import org.appcelerator.titanium.view.TiInsetsProvider;
import org.appcelerator.titanium.view.TiUIView;
@@ -115,6 +116,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.
*
@@ -144,6 +152,13 @@ public abstract class TiUIAbstractTabGroup extends TiUIView
*/
public abstract String getTabTitle(int index);
+ /**
+ * Enables/disables tab menu
+ *
+ * @param enabled value
+ */
+ public abstract void setEnabled(Boolean enabled);
+
// region protected fields
protected final static String TAG = "TiUIAbstractTabGroup";
protected static final String WARNING_LAYOUT_MESSAGE =
@@ -454,7 +469,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();
@@ -495,6 +510,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 +534,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);
}
@@ -552,7 +572,12 @@ public Drawable updateIconTint(TiViewProxy tabProxy, Drawable drawable, boolean
}
// Clone existing drawable so color filter applies correctly.
- drawable = drawable.getConstantState().newDrawable();
+ if (drawable.getConstantState() == null && drawable.getClass() == TiIconDrawable.class) {
+ // TiIconDrawable
+ drawable = drawable.mutate();
+ } else {
+ drawable = drawable.getConstantState().newDrawable();
+ }
final KrollDict tabProperties = tabProxy.getProperties();
final KrollDict properties = getProxy().getProperties();
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
new file mode 100644
index 00000000000..8a7d3105c50
--- /dev/null
+++ b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/tabgroup/TiUIBottomNavigation.java
@@ -0,0 +1,553 @@
+/**
+ * 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.app.Activity;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+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.BottomNavigationMenuView;
+import com.google.android.material.bottomnavigation.BottomNavigationView;
+import com.google.android.material.navigation.NavigationBarView;
+import com.google.android.material.shape.CornerFamily;
+import com.google.android.material.shape.MaterialShapeDrawable;
+import com.google.android.material.shape.ShapeAppearanceModel;
+
+import org.appcelerator.titanium.TiApplication;
+import org.appcelerator.titanium.TiBaseActivity;
+import org.appcelerator.titanium.TiC;
+import org.appcelerator.titanium.TiDimension;
+import org.appcelerator.titanium.proxy.TiViewProxy;
+import org.appcelerator.titanium.util.TiConvert;
+import org.appcelerator.titanium.util.TiIconDrawable;
+import org.appcelerator.titanium.util.TiRHelper;
+import org.appcelerator.titanium.util.TiUIHelper;
+import org.appcelerator.titanium.view.TiUIView;
+
+import java.util.ArrayList;
+
+import ti.modules.titanium.ui.TabGroupProxy;
+import ti.modules.titanium.ui.TabProxy;
+
+/**
+ * TabGroup implementation using BottomNavigationView as a controller.
+ */
+public class TiUIBottomNavigation extends TiUIAbstractTabGroup implements BottomNavigationView.OnItemSelectedListener
+{
+
+ protected final static String TAG = "TiUIBottomNavigation";
+ static int id_layout = 0;
+ static int id_content = 0;
+ static int id_bottomNavigation = 0;
+ private int currentlySelectedIndex = -1;
+ private ArrayList