Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5dbc32d9 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes from topic "change_barchart_api"

* changes:
  Add an empty text when there is no bar view
  Create a BarChartInfo builder
parents 4e07b513 ff3ba834
Loading
Loading
Loading
Loading
+39 −23
Original line number Diff line number Diff line
@@ -32,6 +32,13 @@
        android:gravity="center"
        android:textAppearance="@style/BarChart.Text.HeaderTitle"/>

    <LinearLayout
        android:id="@+id/bar_views_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
@@ -61,5 +68,14 @@
            android:layout_width="wrap_content"
            android:layout_height="48dp"
            android:gravity="center"/>
    </LinearLayout>

    <TextView
        android:id="@+id/empty_view"
        android:layout_width="match_parent"
        android:layout_height="@dimen/settings_bar_view_max_height"
        android:gravity="center"
        android:visibility="gone"
        android:textAppearance="@style/BarChart.Text.Summary"/>

</LinearLayout>
+176 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.
 */

package com.android.settingslib.widget;

import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;

import java.util.ArrayList;
import java.util.List;

/**
 * BarChartInfo is responsible for storing information about {@link BarChartPreference}.
 */
public class BarChartInfo {
    @StringRes
    private final int mTitle;
    @StringRes
    private final int mDetails;
    @StringRes
    private final int mEmptyText;
    private final View.OnClickListener mDetailsOnClickListener;

    private BarViewInfo[] mBarViewInfos;

    /**
     * Gets the resource id for the title shown in {@link BarChartPreference}.
     *
     * @return the string resource id for title.
     */
    public int getTitle() {
        return mTitle;
    }

    /**
     * Gets the resource id for the details shown in {@link BarChartPreference}.
     *
     * @return the string resource id for details.
     */
    public int getDetails() {
        return mDetails;
    }

    /**
     * Gets the resource id for the empty text shown in {@link BarChartPreference} when there is no
     * any bar view in {@link BarChartPreference}.
     *
     * @return the string resource id for empty text.
     */
    public int getEmptyText() {
        return mEmptyText;
    }

    /**
     * Gets the click listener for the details view.
     *
     * @return click listener for details view.
     */
    public View.OnClickListener getDetailsOnClickListener() {
        return mDetailsOnClickListener;
    }

    /**
     * Gets an array which contains up to four {@link BarViewInfo}
     *
     * @return an array holding the current all {@link BarViewInfo} state of the bar chart.
     */
    public BarViewInfo[] getBarViewInfos() {
        return mBarViewInfos;
    }

    void setBarViewInfos(BarViewInfo[] barViewInfos) {
        mBarViewInfos = barViewInfos;
    }

    private BarChartInfo(Builder builder) {
        mTitle = builder.mTitle;
        mDetails = builder.mDetails;
        mEmptyText = builder.mEmptyText;
        mDetailsOnClickListener = builder.mDetailsOnClickListener;

        if (builder.mBarViewInfos != null) {
            mBarViewInfos = builder.mBarViewInfos.stream().toArray(BarViewInfo[]::new);
        }
    }

    /**
     * Builder class for {@link BarChartInfo}
     */
    public static class Builder {
        @StringRes
        private int mTitle;
        @StringRes
        private int mDetails;
        @StringRes
        private int mEmptyText;
        private View.OnClickListener mDetailsOnClickListener;
        private List<BarViewInfo> mBarViewInfos;

        /**
         * Creates an instance of a {@link BarChartInfo} based on the current builder settings.
         *
         * @return The {@link BarChartInfo}.
         */
        public BarChartInfo build() {
            if (mTitle == 0) {
                throw new IllegalStateException("You must call Builder#setTitle() once.");
            }
            return new BarChartInfo(this);
        }

        /**
         * Sets the string resource id for the title.
         */
        public Builder setTitle(@StringRes int title) {
            mTitle = title;
            return this;
        }

        /**
         * Sets the string resource id for the details.
         */
        public Builder setDetails(@StringRes int details) {
            mDetails = details;
            return this;
        }

        /**
         * Sets the string resource id for the empty text.
         */
        public Builder setEmptyText(@StringRes int emptyText) {
            mEmptyText = emptyText;
            return this;
        }

        /**
         * Sets the click listener for details view.
         */
        public Builder setDetailsOnClickListener(
                @Nullable View.OnClickListener clickListener) {
            mDetailsOnClickListener = clickListener;
            return this;
        }

        /**
         * Adds a {@link BarViewInfo} for {@link BarChartPreference}.
         * Maximum of 4 {@link BarViewInfo} can be added.
         */
        public Builder addBarViewInfo(@NonNull BarViewInfo barViewInfo) {
            if (mBarViewInfos == null) {
                mBarViewInfos = new ArrayList<>();
            }
            if (mBarViewInfos.size() >= BarChartPreference.MAXIMUM_BAR_VIEWS) {
                throw new IllegalStateException("We only support up to four bar views");
            }
            mBarViewInfos.add(barViewInfo);
            return this;
        }
    }
}
+88 −59
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 * Copyright (C) 2019 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.
@@ -24,7 +24,6 @@ import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;

@@ -41,31 +40,45 @@ import java.util.Arrays;
 *        android:key="bar_chart"/&gt;
 * </pre>
 *
 * <p>This code sample demonstrates how to initialize the contents of the BarChartPreference defined
 * in the previous XML layout:
 * <p>This code sample demonstrates how to initialize the contents of the BarChartPreference
 * defined in the previous XML layout:
 *
 * <pre>
 * BarViewInfo[] viewsInfo = new BarViewInfo [] {
 *     new BarViewInfo(icon, 18, res of summary),
 *     new BarViewInfo(icon, 25, res of summary),
 *     new BarViewInfo(icon, 10, res of summary),
 *     new BarViewInfo(icon, 3, res of summary),
 *  };
 * BarChartPreference preference = ((BarChartPreference) findPreference("bar_chart"));
 *
 * BarChartInfo info = new BarChartInfo.Builder()
 *     .setTitle(R.string.permission_bar_chart_title)
 *     .setDetails(R.string.permission_bar_chart_details)
 *     .setEmptyText(R.string.permission_bar_chart_empty_text)
 *     .addBarViewInfo(new barViewInfo(...))
 *     .addBarViewInfo(new barViewInfo(...))
 *     .addBarViewInfo(new barViewInfo(...))
 *     .addBarViewInfo(new barViewInfo(...))
 *     .setDetailsOnClickListener(v -> doSomething())
 *     .build();
 *
 * preference.initializeBarChart(info);
 * </pre>
 *
 *
 * <p>You also can update new information for bar views by
 * {@link BarChartPreference#setBarViewInfos(BarViewInfo[])}
 *
 * <pre>
 * BarChartPreference preference = ((BarChartPreference) findPreference("bar_chart"));
 * BarViewInfo[] barViewsInfo = new BarViewInfo [] {
 *     new BarViewInfo(...),
 *     new BarViewInfo(...),
 *     new BarViewInfo(...),
 *     new BarViewInfo(...),
 * };
 *
 * preference.setBarChartTitleRes(R.string.title_res);
 * preference.setBarChartDetailsRes(R.string.details_res);
 * preference.setBarChartDetailsClickListener(v -> doSomething());
 * preference.setAllBarViewsData(viewsInfo);
 * preference.setBarViewInfos(barViewsInfo);
 * </pre>
 */
public class BarChartPreference extends Preference {

    static final int MAXIMUM_BAR_VIEWS = 4;
    private static final String TAG = "BarChartPreference";
    private static final int MAXIMUM_BAR_VIEWS = 4;
    private static final int[] BAR_VIEWS = {
            R.id.bar_view1,
            R.id.bar_view2,
@@ -74,12 +87,7 @@ public class BarChartPreference extends Preference {
    };

    private int mMaxBarHeight;
    @StringRes
    private int mTitleId;
    @StringRes
    private int mDetailsId;
    private BarViewInfo[] mBarViewsInfo;
    private View.OnClickListener mDetailsOnClickListener;
    private BarChartInfo mBarChartInfo;

    public BarChartPreference(Context context) {
        super(context);
@@ -103,40 +111,26 @@ public class BarChartPreference extends Preference {
    }

    /**
     * Set the text resource for bar chart title.
     */
    public void setBarChartTitle(@StringRes int resId) {
        mTitleId = resId;
        notifyChanged();
    }

    /**
     * Set the text resource for bar chart details.
     */
    public void setBarChartDetails(@StringRes int resId) {
        mDetailsId = resId;
        notifyChanged();
    }

    /**
     * Register a callback to be invoked when bar chart details view is clicked.
     * According to the information in {@link BarChartInfo} to initialize bar chart.
     *
     * @param barChartInfo The barChartInfo contains title, details, empty text, click listener
     *                     attached on details view and four bar views.
     */
    public void setBarChartDetailsClickListener(@Nullable View.OnClickListener clickListener) {
        mDetailsOnClickListener = clickListener;
    public void initializeBarChart(@NonNull BarChartInfo barChartInfo) {
        mBarChartInfo = barChartInfo;
        notifyChanged();
    }

    /**
     * Set all bar view information which you'd like to show in preference.
     * Sets all bar view information which you'd like to show in preference.
     *
     * @param barViewsInfo the barViewsInfo contain at least one {@link BarViewInfo}.
     * @param barViewInfos the barViewInfos contain at least one {@link BarViewInfo}.
     */
    public void setAllBarViewsInfo(@NonNull BarViewInfo[] barViewsInfo) {
        mBarViewsInfo = barViewsInfo;
        // Do a sort in descending order, the first element would have max {@link
        // BarViewInfo#mBarNumber}
        Arrays.sort(mBarViewsInfo);
        calculateAllBarViewHeights();
    public void setBarViewInfos(@Nullable BarViewInfo[] barViewInfos) {
        if (barViewInfos != null && barViewInfos.length > MAXIMUM_BAR_VIEWS) {
            throw new IllegalStateException("We only support up to four bar views");
        }
        mBarChartInfo.setBarViewInfos(barViewInfos);
        notifyChanged();
    }

@@ -146,7 +140,17 @@ public class BarChartPreference extends Preference {
        holder.setDividerAllowedAbove(true);
        holder.setDividerAllowedBelow(true);

        // We must show title of bar chart.
        bindChartTitleView(holder);

        final BarViewInfo[] barViewInfos = mBarChartInfo.getBarViewInfos();
        // If there is no any bar view, we just show an empty text.
        if (barViewInfos == null || barViewInfos.length == 0) {
            setEmptyViewVisible(holder, true /* visible */);
            return;
        }
        setEmptyViewVisible(holder, false /* visible */);

        bindChartDetailsView(holder);
        updateBarChart(holder);
    }
@@ -160,43 +164,68 @@ public class BarChartPreference extends Preference {

    private void bindChartTitleView(PreferenceViewHolder holder) {
        final TextView titleView = (TextView) holder.findViewById(R.id.bar_chart_title);
        titleView.setText(mTitleId);
        titleView.setText(mBarChartInfo.getTitle());
    }

    private void bindChartDetailsView(PreferenceViewHolder holder) {
        final Button detailsView = (Button) holder.findViewById(R.id.bar_chart_details);
        if (mDetailsId == 0) {
        final int details = mBarChartInfo.getDetails();
        if (details == 0) {
            detailsView.setVisibility(View.GONE);
        } else {
            detailsView.setVisibility(View.VISIBLE);
            detailsView.setText(mDetailsId);
            detailsView.setOnClickListener(mDetailsOnClickListener);
            detailsView.setText(details);
            detailsView.setOnClickListener(mBarChartInfo.getDetailsOnClickListener());
        }
    }

    private void updateBarChart(PreferenceViewHolder holder) {
        normalizeBarViewHeights();

        final BarViewInfo[] barViewInfos = mBarChartInfo.getBarViewInfos();

        for (int index = 0; index < MAXIMUM_BAR_VIEWS; index++) {
            final BarView barView = (BarView) holder.findViewById(BAR_VIEWS[index]);

            // If there is no bar views data can be shown.
            if (mBarViewsInfo == null || index >= mBarViewsInfo.length) {
            // If there is no bar view info can be shown.
            if (barViewInfos == null || index >= barViewInfos.length) {
                barView.setVisibility(View.GONE);
                continue;
            }
            barView.setVisibility(View.VISIBLE);
            barView.updateView(mBarViewsInfo[index]);
            barView.updateView(barViewInfos[index]);
        }
    }

    private void calculateAllBarViewHeights() {
    private void normalizeBarViewHeights() {
        final BarViewInfo[] barViewInfos = mBarChartInfo.getBarViewInfos();
        // If there is no any bar view info, we don't need to calculate the height of all bar views.
        if (barViewInfos == null || barViewInfos.length == 0) {
            return;
        }
        // Do a sort in descending order, the first element would have max {@link
        // BarViewInfo#mHeight}
        Arrays.sort(barViewInfos);
        // Since we sorted this array in advance, the first element must have the max {@link
        // BarViewInfo#mHeight}.
        final int maxBarHeight = mBarViewsInfo[0].getHeight();
        final int maxBarHeight = barViewInfos[0].getHeight();
        // If the max number of bar view is zero, then we don't calculate the unit for bar height.
        final int unit = maxBarHeight == 0 ? 0 : mMaxBarHeight / maxBarHeight;

        for (BarViewInfo barView : mBarViewsInfo) {
        for (BarViewInfo barView : barViewInfos) {
            barView.setNormalizedHeight(barView.getHeight() * unit);
        }
    }

    private void setEmptyViewVisible(PreferenceViewHolder holder, boolean visible) {
        final View barViewsContainer = holder.findViewById(R.id.bar_views_container);
        final TextView emptyView = (TextView) holder.findViewById(R.id.empty_view);
        final int emptyTextRes = mBarChartInfo.getEmptyText();

        if (emptyTextRes != 0) {
            emptyView.setText(emptyTextRes);
        }
        emptyView.setVisibility(visible ? View.VISIBLE : View.GONE);
        barViewsContainer.setVisibility(visible ? View.GONE : View.VISIBLE);
    }
}
+129 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.
 */

package com.android.settingslib.widget;

import static com.google.common.truth.Truth.assertThat;

import android.view.View;

import androidx.annotation.StringRes;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;

@RunWith(RobolectricTestRunner.class)
public class BarChartInfoTest {

    @Rule
    public final ExpectedException thrown = ExpectedException.none();
    @StringRes
    private final int mTitle = 0x11111111;
    @StringRes
    private final int mDetails = 0x22222222;
    @StringRes
    private final int mEmptyText = 0x33333333;
    private final View.OnClickListener mClickListener = v -> {
    };

    @Test
    public void builder_shouldSetFieldsInTheInfo() {
        final BarChartInfo barChartInfo = new BarChartInfo.Builder()
                .setTitle(mTitle)
                .setDetails(mDetails)
                .setEmptyText(mEmptyText)
                .setDetailsOnClickListener(mClickListener)
                .build();
        assertThat(barChartInfo.getTitle()).isEqualTo(mTitle);
        assertThat(barChartInfo.getDetails()).isEqualTo(mDetails);
        assertThat(barChartInfo.getEmptyText()).isEqualTo(mEmptyText);
        assertThat(barChartInfo.getDetailsOnClickListener()).isEqualTo(mClickListener);
    }

    @Test
    public void builder_noTitle_shouldThrowIllegalStateException() {
        thrown.expect(IllegalStateException.class);

        new BarChartInfo.Builder()
                .setDetails(mDetails)
                .setEmptyText(mEmptyText)
                .setDetailsOnClickListener(mClickListener)
                .build();
    }

    @Test
    public void addBarViewInfo_oneBarViewInfo_shouldSetOneBarViewInfo() {
        final BarViewInfo barViewInfo = new BarViewInfo(
                null /* icon */,
                50,
                mTitle);

        final BarChartInfo mBarChartInfo = new BarChartInfo.Builder()
                .setTitle(mTitle)
                .setDetails(mDetails)
                .setEmptyText(mEmptyText)
                .setDetailsOnClickListener(mClickListener)
                .addBarViewInfo(barViewInfo)
                .build();

        assertThat(mBarChartInfo.getBarViewInfos().length).isEqualTo(1);
        assertThat(mBarChartInfo.getBarViewInfos()[0]).isEqualTo(barViewInfo);
    }

    @Test
    public void addBarViewInfo_maxNumberOfInfoAllowed_shouldSetMaxBarViewInfos() {
        final BarViewInfo barViewInfo = new BarViewInfo(
                null /* icon */,
                50,
                mTitle);
        final BarChartInfo mBarChartInfo = new BarChartInfo.Builder()
                .setTitle(mTitle)
                .setDetails(mDetails)
                .setEmptyText(mEmptyText)
                .setDetailsOnClickListener(mClickListener)
                .addBarViewInfo(barViewInfo)
                .addBarViewInfo(barViewInfo)
                .addBarViewInfo(barViewInfo)
                .addBarViewInfo(barViewInfo)
                .build();

        assertThat(mBarChartInfo.getBarViewInfos().length).isEqualTo(4);
    }

    @Test
    public void addBarViewInfo_moreInfosThanMaxAllowed_shouldThrowIllegalStateException() {
        thrown.expect(IllegalStateException.class);

        final BarViewInfo barViewInfo = new BarViewInfo(
                null /* icon */,
                50,
                mTitle);
        new BarChartInfo.Builder()
                .setTitle(mTitle)
                .setDetails(mDetails)
                .setEmptyText(mEmptyText)
                .setDetailsOnClickListener(mClickListener)
                .addBarViewInfo(barViewInfo)
                .addBarViewInfo(barViewInfo)
                .addBarViewInfo(barViewInfo)
                .addBarViewInfo(barViewInfo)
                .addBarViewInfo(barViewInfo)
                .build();
    }
}
+103 −42

File changed.

Preview size limit exceeded, changes collapsed.