Commit 58f3edcd authored by Michael W's avatar Michael W
Browse files

DeskClock: Move navigation bar to bottom



* Wrap the desk_clock layout into a LinearLayout so the Snackbar
  appears above the BottomNavigationView
* Change the icon tint and text color to reflect the changes for the dark
  layout
* Use a BottomNavigationView instead of a TabLayout
* Reorder imports - looks like someone didn't care before
Co-authored-by: default avatarArian <arian.kulmer@web.de>
Co-authored-by: default avatarJesse Chan <jc@lineageos.org>
Signed-off-by: default avatarJesse Chan <jc@lineageos.org>
Change-Id: I780713dcbeb58256b2660a9631d48e5f7259fb11
parent 9026139e
......@@ -15,7 +15,7 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:color="@color/white" />
<item android:state_selected="true" android:color="@color/white" />
<item android:color="@color/white_63p" />
</selector>
\ No newline at end of file
<item android:state_focused="true" android:color="@color/accent_color" />
<item android:state_selected="true" android:color="@color/accent_color" />
<item android:color="@color/white_50p" />
</selector>
......@@ -15,117 +15,118 @@
limitations under the License.
-->
<androidx.coordinatorlayout.widget.CoordinatorLayout
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:statusBarBackground="@null">
android:orientation="vertical"
app:statusBarBackground="@null"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.AppBarLayout
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
app:elevation="0dp">
android:layout_height="0dp"
android:layout_weight="1">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:contentInsetStart="0dp"
tools:ignore="RtlSymmetry">
android:background="@null"
app:elevation="0dp">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:tabGravity="fill"
app:tabIndicatorColor="@android:color/transparent"
app:tabMaxWidth="0dp"
app:tabMode="fixed"
app:tabPaddingEnd="0dp"
app:tabPaddingStart="0dp" />
</androidx.appcompat.widget.Toolbar>
<View
android:id="@+id/tab_hairline"
android:layout_width="match_parent"
android:layout_height="@dimen/hairline_height"
android:layout_gravity="bottom"
android:background="@color/hairline"
android:importantForAccessibility="no" />
android:layout_height="wrap_content"
app:contentInsetStart="0dp"
tools:ignore="RtlSymmetry"
android:gravity="center">
</com.google.android.material.appbar.AppBarLayout>
<TextView
android:id="@+id/title_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/desk_clock_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
android:saveEnabled="false" />
android:saveEnabled="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<include layout="@layout/drop_shadow" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:baselineAligned="false"
android:orientation="horizontal"
app:layout_behavior="com.android.deskclock.widget.toast.SnackbarSlidingBehavior">
</FrameLayout>
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="start|center_vertical"
android:layout_weight="1">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:baselineAligned="false"
android:orientation="horizontal"
app:layout_behavior="com.android.deskclock.widget.toast.SnackbarSlidingBehavior">
<Button
android:id="@+id/left_button"
style="?attr/borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:scaleType="centerInside" />
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="start|center_vertical"
android:layout_weight="1">
</FrameLayout>
<Button
android:id="@+id/left_button"
style="?attr/borderlessButtonStyle"
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:scaleType="centerInside" />
android:layout_margin="@dimen/fab_margin"
app:borderWidth="0dp"
app:elevation="@dimen/fab_elevation" />
</FrameLayout>
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="end|center_vertical"
android:layout_weight="1">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="@dimen/fab_margin"
app:borderWidth="0dp"
app:elevation="@dimen/fab_elevation" />
<Button
android:id="@+id/right_button"
style="?attr/borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:scaleType="centerInside" />
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="end|center_vertical"
android:layout_weight="1">
</FrameLayout>
<Button
android:id="@+id/right_button"
style="?attr/borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:scaleType="centerInside" />
</LinearLayout>
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/content"
android:background="@color/secondary_color"
app:menu="@menu/bottom_navigation_menu"
app:itemIconTint="@color/tab_tint_color"
app:itemTextColor="@color/tab_tint_color"
app:itemTextAppearanceActive="@style/navText"
app:itemTextAppearanceInactive="@style/navText"
app:labelVisibilityMode="labeled" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="10dp"
android:gravity="center"
android:includeFontPadding="false"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.Tab" />
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/page_alarm"
android:enabled="true"
android:icon="@drawable/ic_tab_alarm"
android:title="@string/menu_alarm"/>
<item
android:id="@+id/page_clock"
android:enabled="true"
android:icon="@drawable/ic_tab_clock"
android:title="@string/menu_clock"/>
<item
android:id="@+id/page_timer"
android:enabled="true"
android:icon="@drawable/ic_tab_timer"
android:title="@string/menu_timer"/>
<item
android:id="@+id/page_stopwatch"
android:enabled="true"
android:icon="@drawable/ic_tab_stopwatch"
android:title="@string/menu_stopwatch"/>
</menu>
......@@ -18,4 +18,5 @@
<resources>
<color name="default_background">#121212</color>
<color name="accent_color">#FEF177</color>
<color name="secondary_color">#212121</color>
</resources>
......@@ -24,6 +24,7 @@
<color name="white">#FFFFFFFF</color>
<color name="white_08p">#14FFFFFF</color>
<color name="white_63p">#A0FFFFFF</color>
<color name="white_50p">#80FFFFFF</color>
<color name="no_alarms">#4CFFFFFF</color>
<color name="transparent">#00000000</color>
......@@ -38,4 +39,5 @@
<color name="widget_shadow_color">#000000</color>
<color name="accent_color">#FFEB40</color>
<color name="secondary_color">#2F378A</color>
</resources>
......@@ -98,8 +98,7 @@
<style name="SecondaryLabelTextAppearance" parent="SecondaryLabelTextParentAppearance" />
<style name="TextAppearance.Tab" parent="TextAppearance.Design.Tab">
<item name="android:fontFamily">sans-serif-medium</item>
<style name="navText">
<item name="android:textSize">12sp</item>
</style>
......@@ -133,7 +132,6 @@
<item name="android:headerBackground">@android:color/transparent</item>
<item name="android:numbersBackgroundColor">@android:color/transparent</item>
<item name="android:numbersSelectorColor">?attr/colorAccent</item>
<item name="android:numbersTextColor">?android:attr/textColorPrimary</item>
</style>
</resources>
......@@ -25,13 +25,6 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import androidx.annotation.StringRes;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayout;
import androidx.viewpager.widget.ViewPager;
import androidx.viewpager.widget.ViewPager.OnPageChangeListener;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
......@@ -40,6 +33,12 @@ import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
import androidx.viewpager.widget.ViewPager;
import androidx.viewpager.widget.ViewPager.OnPageChangeListener;
import com.android.deskclock.actionbarmenu.MenuItemControllerFactory;
import com.android.deskclock.actionbarmenu.NightModeMenuItemController;
......@@ -55,6 +54,9 @@ import com.android.deskclock.uidata.TabListener;
import com.android.deskclock.uidata.UiDataModel;
import com.android.deskclock.widget.toast.SnackbarManager;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.snackbar.Snackbar;
import static androidx.viewpager.widget.ViewPager.SCROLL_STATE_DRAGGING;
import static androidx.viewpager.widget.ViewPager.SCROLL_STATE_IDLE;
import static androidx.viewpager.widget.ViewPager.SCROLL_STATE_SETTLING;
......@@ -114,17 +116,17 @@ public class DeskClock extends BaseActivity
/** The button right of the {@link #mFab} shared across all tabs in the user interface. */
private Button mRightButton;
/** The controller that shows the drop shadow when content is not scrolled to the top. */
private DropShadowController mDropShadowController;
/** The ViewPager that pages through the fragments representing the content of the tabs. */
private ViewPager mFragmentTabPager;
/** Generates the fragments that are displayed by the {@link #mFragmentTabPager}. */
private FragmentTabPagerAdapter mFragmentTabPagerAdapter;
/** The container that stores the tab headers. */
private TabLayout mTabLayout;
/** The view that displays the current tab's title */
private TextView mTitleView;
/** The bottom navigation bar */
private BottomNavigationView mBottomNavigation;
/** {@code true} when a settings change necessitates recreating this activity. */
private boolean mRecreateActivity;
......@@ -170,43 +172,6 @@ public class DeskClock extends BaseActivity
// inflation occurs *after* the initial draw and a second layout pass adds in the menu.
onCreateOptionsMenu(toolbar.getMenu());
// Create the tabs that make up the user interface.
mTabLayout = (TabLayout) findViewById(R.id.tabs);
final int tabCount = UiDataModel.getUiDataModel().getTabCount();
final boolean showTabLabel = getResources().getBoolean(R.bool.showTabLabel);
final boolean showTabHorizontally = getResources().getBoolean(R.bool.showTabHorizontally);
for (int i = 0; i < tabCount; i++) {
final UiDataModel.Tab tabModel = UiDataModel.getUiDataModel().getTab(i);
final @StringRes int labelResId = tabModel.getLabelResId();
final TabLayout.Tab tab = mTabLayout.newTab()
.setTag(tabModel)
.setIcon(tabModel.getIconResId())
.setContentDescription(labelResId);
if (showTabLabel) {
tab.setText(labelResId);
tab.setCustomView(R.layout.tab_item);
@SuppressWarnings("ConstantConditions")
final TextView text = (TextView) tab.getCustomView()
.findViewById(android.R.id.text1);
text.setTextColor(mTabLayout.getTabTextColors());
// Bind the icon to the TextView.
final Drawable icon = tab.getIcon();
if (showTabHorizontally) {
// Remove the icon so it doesn't affect the minimum TabLayout height.
tab.setIcon(null);
text.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null);
} else {
text.setCompoundDrawablesRelativeWithIntrinsicBounds(null, icon, null, null);
}
}
mTabLayout.addTab(tab);
}
// Configure the buttons shared by the tabs.
mFab = (ImageView) findViewById(R.id.fab);
mLeftButton = (Button) findViewById(R.id.left_button);
......@@ -296,24 +261,47 @@ public class DeskClock extends BaseActivity
mFragmentTabPager.setAdapter(mFragmentTabPagerAdapter);
// Mirror changes made to the selected tab into UiDataModel.
mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
UiDataModel.getUiDataModel().setSelectedTab((UiDataModel.Tab) tab.getTag());
}
mBottomNavigation = findViewById(R.id.bottom_view);
mBottomNavigation.setOnNavigationItemSelectedListener(mNavigationListener);
@Override
public void onTabUnselected(TabLayout.Tab tab) {
// Honor changes to the selected tab from outside entities.
UiDataModel.getUiDataModel().addTabListener(mTabChangeWatcher);
mTitleView = findViewById(R.id.title_view);
}
private BottomNavigationView.OnNavigationItemSelectedListener mNavigationListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
UiDataModel.Tab tab = null;
switch (item.getItemId()) {
case R.id.page_alarm:
tab = UiDataModel.Tab.ALARMS;
break;
case R.id.page_clock:
tab = UiDataModel.Tab.CLOCKS;
break;
case R.id.page_timer:
tab = UiDataModel.Tab.TIMERS;
break;
case R.id.page_stopwatch:
tab = UiDataModel.Tab.STOPWATCH;
break;
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
if (tab != null) {
UiDataModel.getUiDataModel().setSelectedTab(tab);
return true;
}
});
// Honor changes to the selected tab from outside entities.
UiDataModel.getUiDataModel().addTabListener(mTabChangeWatcher);
}
return false;
}
};
@Override
protected void onStart() {
......@@ -326,10 +314,6 @@ public class DeskClock extends BaseActivity
protected void onResume() {
super.onResume();
final View dropShadow = findViewById(R.id.drop_shadow);
mDropShadowController = new DropShadowController(dropShadow, UiDataModel.getUiDataModel(),
mSnackbarAnchor.findViewById(R.id.tab_hairline));
// ViewPager does not save state; this honors the selected tab in the user interface.
updateCurrentTab();
}
......@@ -352,16 +336,6 @@ public class DeskClock extends BaseActivity
}
}
@Override
public void onPause() {
if (mDropShadowController != null) {
mDropShadowController.stop();
mDropShadowController = null;
}
super.onPause();
}
@Override
protected void onStop() {
DataModel.getDataModel().removeSilentSettingsListener(mSilentSettingChangeWatcher);
......@@ -486,21 +460,14 @@ public class DeskClock extends BaseActivity
}
/**
* Configure the {@link #mFragmentTabPager} and {@link #mTabLayout} to display UiDataModel's
* selected tab.
* Configure the {@link #mFragmentTabPager} and {@link #mBottomNavigation} to display
* UiDataModel's selected tab.
*/
private void updateCurrentTab() {
// Fetch the selected tab from the source of truth: UiDataModel.
final UiDataModel.Tab selectedTab = UiDataModel.getUiDataModel().getSelectedTab();
// Update the selected tab in the tablayout if it does not agree with UiDataModel.
for (int i = 0; i < mTabLayout.getTabCount(); i++) {
final TabLayout.Tab tab = mTabLayout.getTabAt(i);
if (tab != null && tab.getTag() == selectedTab && !tab.isSelected()) {
tab.select();
break;
}
}
// Update the selected tab in the mBottomNavigation if it does not agree with UiDataModel.
mBottomNavigation.setSelectedItemId(selectedTab.getPageResId());
// Update the selected fragment in the viewpager if it does not agree with UiDataModel.
for (int i = 0; i < mFragmentTabPagerAdapter.getCount(); i++) {
......@@ -510,6 +477,8 @@ public class DeskClock extends BaseActivity
break;
}
}
mTitleView.setText(selectedTab.getLabelResId());
}
/**
......
......@@ -19,7 +19,7 @@ package com.android.deskclock.uidata;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Typeface;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntegerRes;
import androidx.annotation.StringRes;
import com.android.deskclock.AlarmClockFragment;
......@@ -39,23 +39,23 @@ public final class UiDataModel {
/** Identifies each of the primary tabs within the application. */
public enum Tab {
ALARMS(AlarmClockFragment.class, R.drawable.ic_tab_alarm, R.string.menu_alarm),
CLOCKS(ClockFragment.class, R.drawable.ic_tab_clock, R.string.menu_clock),
TIMERS(TimerFragment.class, R.drawable.ic_tab_timer, R.string.menu_timer),
STOPWATCH(StopwatchFragment.class, R.drawable.ic_tab_stopwatch, R.string.menu_stopwatch);
ALARMS(AlarmClockFragment.class, R.id.page_alarm, R.string.menu_alarm),
CLOCKS(ClockFragment.class, R.id.page_clock, R.string.menu_clock),
TIMERS(TimerFragment.class, R.id.page_timer, R.string.menu_timer),
STOPWATCH(StopwatchFragment.class, R.id.page_stopwatch, R.string.menu_stopwatch);
private final String mFragmentClassName;
private final @DrawableRes int mIconResId;
private final @IntegerRes int mPageResId;
private final @StringRes int mLabelResId;
Tab(Class fragmentClass, @DrawableRes int iconResId, @StringRes int labelResId) {
Tab(Class fragmentClass, @IntegerRes int pageResId, @StringRes int labelResId) {
mFragmentClassName = fragmentClass.getName();
mIconResId = iconResId;
mPageResId = pageResId;
mLabelResId = labelResId;
}
public String getFragmentClassName() { return mFragmentClassName; }
public @DrawableRes int getIconResId() { return mIconResId; }
public @IntegerRes int getPageResId() { return mPageResId; }
public @StringRes int getLabelResId() { return mLabelResId; }
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment