13da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam/*
240dd08f091020a436af0d253d2cb54f560493920Maurice Lam * Copyright (C) 2014 The Android Open Source Project
33da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam *
43da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam * Licensed under the Apache License, Version 2.0 (the "License");
53da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam * you may not use this file except in compliance with the License.
63da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam * You may obtain a copy of the License at
73da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam *
83da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam *      http://www.apache.org/licenses/LICENSE-2.0
93da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam *
103da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam * Unless required by applicable law or agreed to in writing, software
113da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam * distributed under the License is distributed on an "AS IS" BASIS,
123da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam * See the License for the specific language governing permissions and
143da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam * limitations under the License.
153da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam */
163da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
173da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lampackage com.android.setupwizard.navigationbar;
183da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
193da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lamimport android.app.Activity;
203da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lamimport android.app.Fragment;
213da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lamimport android.content.Context;
223da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lamimport android.content.res.TypedArray;
233da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lamimport android.graphics.Color;
243da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lamimport android.os.Bundle;
253da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lamimport android.util.AttributeSet;
263da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lamimport android.view.ContextThemeWrapper;
273da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lamimport android.view.LayoutInflater;
283da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lamimport android.view.View;
293da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lamimport android.view.View.OnClickListener;
303da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lamimport android.view.ViewGroup;
313da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lamimport android.view.ViewTreeObserver;
323da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lamimport android.view.ViewTreeObserver.OnPreDrawListener;
333da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lamimport android.widget.Button;
343da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
353da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam/**
36d9fbe9e218ce792755341aea4cd9c678171c5669Maurice Lam * Fragment class for controlling the custom navigation bar shown during setup wizard. Apps in the
37d9fbe9e218ce792755341aea4cd9c678171c5669Maurice Lam * Android tree can use this by including the common.mk makefile. Apps outside of the tree can
38d9fbe9e218ce792755341aea4cd9c678171c5669Maurice Lam * create a library project out of the source.
393da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam */
406c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lampublic class SetupWizardNavBar extends Fragment implements OnPreDrawListener, OnClickListener {
413da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    private static final String TAG = "SetupWizardNavBar";
423da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    private static final int IMMERSIVE_FLAGS =
433da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam            View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
44f24653c485c73b8f4e8b947cfd2dbed8d79aa58eMaurice Lam    private int mSystemUiFlags = IMMERSIVE_FLAGS | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
453da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
463da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    private ViewGroup mNavigationBarView;
473da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    private Button mNextButton;
483da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    private Button mBackButton;
493da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    private NavigationBarListener mCallback;
503da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
513da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    public interface NavigationBarListener {
523da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        public void onNavigationBarCreated(SetupWizardNavBar bar);
533da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        public void onNavigateBack();
543da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        public void onNavigateNext();
553da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    }
563da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
573da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    public SetupWizardNavBar() {
583da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        // no-arg constructor for fragments
593da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    }
603da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
613da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    @Override
623da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    public void onAttach(Activity activity) {
633da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        super.onAttach(activity);
646c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lam        mCallback = (NavigationBarListener) activity;
653da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    }
663da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
673da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    @Override
683da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    public View onCreateView(LayoutInflater inflater, ViewGroup container,
693da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam            Bundle savedInstanceState) {
703da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        Context context = new ContextThemeWrapper(getActivity(), getNavbarTheme());
713da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        inflater = LayoutInflater.from(context);
723da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        mNavigationBarView = (ViewGroup) inflater.inflate(R.layout.setup_wizard_navbar_layout,
733da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam                container, false);
746c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lam        mNextButton = (Button) mNavigationBarView.findViewById(R.id.setup_wizard_navbar_next);
756c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lam        mBackButton = (Button) mNavigationBarView.findViewById(R.id.setup_wizard_navbar_back);
766c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lam        mNextButton.setOnClickListener(this);
776c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lam        mBackButton.setOnClickListener(this);
783da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        return mNavigationBarView;
793da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    }
803da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
813da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    @Override
823da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    public void onViewCreated(View view, Bundle savedInstanceState) {
833da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        super.onViewCreated(view, savedInstanceState);
843da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        mCallback.onNavigationBarCreated(this);
853da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        mNavigationBarView.setSystemUiVisibility(mSystemUiFlags);
863da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
873da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        // Set the UI flags before draw because the visibility might change in unexpected /
883da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        // undetectable times, like transitioning from a finishing activity that had a keyboard
893da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        ViewTreeObserver viewTreeObserver = mNavigationBarView.getViewTreeObserver();
903da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        viewTreeObserver.addOnPreDrawListener(this);
913da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    }
923da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
933da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    @Override
943da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    public boolean onPreDraw() {
953da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        // View.setSystemUiVisibility checks if the visibility changes before applying them
963da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        // so the performance impact is contained
973da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        mNavigationBarView.setSystemUiVisibility(mSystemUiFlags);
983da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        return true;
993da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    }
1003da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
1013da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    /**
1023da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam     * Sets whether system navigation bar should be hidden.
1033da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam     * @param useImmersiveMode True to activate immersive mode and hide the system navigation bar
1043da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam     */
1053da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    public void setUseImmersiveMode(boolean useImmersiveMode) {
106f24653c485c73b8f4e8b947cfd2dbed8d79aa58eMaurice Lam        // By default, enable layoutHideNavigation if immersive mode is used
107f24653c485c73b8f4e8b947cfd2dbed8d79aa58eMaurice Lam        setUseImmersiveMode(useImmersiveMode, useImmersiveMode);
108f24653c485c73b8f4e8b947cfd2dbed8d79aa58eMaurice Lam    }
109f24653c485c73b8f4e8b947cfd2dbed8d79aa58eMaurice Lam
110f24653c485c73b8f4e8b947cfd2dbed8d79aa58eMaurice Lam    public void setUseImmersiveMode(boolean useImmersiveMode, boolean layoutHideNavigation) {
111f24653c485c73b8f4e8b947cfd2dbed8d79aa58eMaurice Lam        if (useImmersiveMode) {
112f24653c485c73b8f4e8b947cfd2dbed8d79aa58eMaurice Lam            mSystemUiFlags |= IMMERSIVE_FLAGS;
113f24653c485c73b8f4e8b947cfd2dbed8d79aa58eMaurice Lam            if (layoutHideNavigation) {
114f24653c485c73b8f4e8b947cfd2dbed8d79aa58eMaurice Lam                mSystemUiFlags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
115f24653c485c73b8f4e8b947cfd2dbed8d79aa58eMaurice Lam            }
116f24653c485c73b8f4e8b947cfd2dbed8d79aa58eMaurice Lam        } else {
117f24653c485c73b8f4e8b947cfd2dbed8d79aa58eMaurice Lam            mSystemUiFlags &= ~(IMMERSIVE_FLAGS | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
118f24653c485c73b8f4e8b947cfd2dbed8d79aa58eMaurice Lam        }
1193da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        mNavigationBarView.setSystemUiVisibility(mSystemUiFlags);
1203da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    }
1213da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
1223da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    private int getNavbarTheme() {
1236c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lam        // Normally we can automatically guess the theme by comparing the foreground color against
1246c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lam        // the background color. But we also allow specifying explicitly using
1256c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lam        // setup_wizard_navbar_theme.
1263da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        TypedArray attributes = getActivity().obtainStyledAttributes(
1273da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam                new int[] {
1283da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam                        R.attr.setup_wizard_navbar_theme,
1293da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam                        android.R.attr.colorForeground,
1303da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam                        android.R.attr.colorBackground });
1313da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        int theme = attributes.getResourceId(0, 0);
1323da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        if (theme == 0) {
1336c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lam            // Compare the value of the foreground against the background color to see if current
1346c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lam            // theme is light-on-dark or dark-on-light.
1353da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam            float[] foregroundHsv = new float[3];
1363da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam            float[] backgroundHsv = new float[3];
1373da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam            Color.colorToHSV(attributes.getColor(1, 0), foregroundHsv);
1383da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam            Color.colorToHSV(attributes.getColor(2, 0), backgroundHsv);
1393da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam            boolean isDarkBg = foregroundHsv[2] > backgroundHsv[2];
1403da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam            theme = isDarkBg ? R.style.setup_wizard_navbar_theme_dark :
1413da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam                    R.style.setup_wizard_navbar_theme_light;
1423da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        }
1433da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        attributes.recycle();
1443da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        return theme;
1453da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    }
1463da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
1476c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lam    @Override
1486c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lam    public void onClick(View v) {
1496c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lam        if (v == mBackButton) {
1506c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lam            mCallback.onNavigateBack();
1516c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lam        } else if (v == mNextButton) {
1526c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lam            mCallback.onNavigateNext();
1536c4d5786ec0ce22429a68c5b9e92f3dab9863f2dMaurice Lam        }
1543da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    }
1553da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
1563da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    public Button getBackButton() {
1573da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        return mBackButton;
1583da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    }
1593da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
1603da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    public Button getNextButton() {
1613da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        return mNextButton;
1623da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    }
1633da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
1643da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    public static class NavButton extends Button {
1653da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
1663da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        public NavButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
1673da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam            super(context, attrs, defStyleAttr, defStyleRes);
1683da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        }
1693da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
1703da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        public NavButton(Context context, AttributeSet attrs, int defStyleAttr) {
1713da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam            super(context, attrs, defStyleAttr);
1723da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        }
1733da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
1743da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        public NavButton(Context context, AttributeSet attrs) {
1753da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam            super(context, attrs);
1763da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        }
1773da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
1783da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        public NavButton(Context context) {
1793da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam            super(context);
1803da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        }
1813da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
1823da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        @Override
1833da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        public void setEnabled(boolean enabled) {
1843da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam            super.setEnabled(enabled);
185fc4414955c73b11a7975c74a741df00c7b3a005bMaurice Lam            // The color of the button is #de000000 / #deffffff when enabled. When disabled, apply
186fc4414955c73b11a7975c74a741df00c7b3a005bMaurice Lam            // additional 23% alpha, so the overall opacity is 20%.
187fc4414955c73b11a7975c74a741df00c7b3a005bMaurice Lam            setAlpha(enabled ? 1.0f : 0.23f);
1883da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam        }
1893da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam    }
1903da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam
1913da01bb1e0865edc45a4cebe122c4ab1f1ba0bacMaurice Lam}
192