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

Commit 9b24df2c authored by Zaiyue Xue's avatar Zaiyue Xue Committed by Android (Google) Code Review
Browse files

Merge changes from topic "accessibility1" into tm-qpr-dev

* changes:
  Support accessibility for battery chart (4)
  Support accessibility for battery chart (3)
  Support accessibility for battery chart (2)
  Support accessibility for battery chart (1)
parents 676bcedb 525fd2dc
Loading
Loading
Loading
Loading
+64 −42
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.View;
import android.view.accessibility.AccessibilityManager;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -107,12 +108,9 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
    private boolean mIs24HourFormat;
    private boolean mIsFooterPrefAdded = false;
    private View mBatteryChartViewGroup;
    private View mCategoryTitleView;
    private PreferenceScreen mPreferenceScreen;
    private FooterPreference mFooterPreference;
    // Daily view model only saves abbreviated day of week texts (e.g. MON). This field saves the
    // full day of week texts (e.g. Monday), which is used in category title and battery detail
    // page.
    private List<String> mDailyTimestampFullTexts;
    private BatteryChartViewModel mDailyViewModel;
    private List<BatteryChartViewModel> mHourlyViewModels;

@@ -127,6 +125,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
    private final AnimatorListenerAdapter mHourlyChartFadeOutAdapter =
            createHourlyChartAnimatorListenerAdapter(/*isToShow=*/ false);

    @VisibleForTesting
    final DailyChartLabelTextGenerator mDailyChartLabelTextGenerator =
            new DailyChartLabelTextGenerator();
    @VisibleForTesting
    final HourlyChartLabelTextGenerator mHourlyChartLabelTextGenerator =
            new HourlyChartLabelTextGenerator();

    // Preference cache to avoid create new instance each time.
    @VisibleForTesting
    final Map<String, Preference> mPreferenceCache = new HashMap<>();
@@ -284,29 +289,24 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
                getTotalHours(batteryLevelData));

        if (batteryLevelData == null) {
            mDailyTimestampFullTexts = null;
            mDailyViewModel = null;
            mHourlyViewModels = null;
            refreshUi();
            return;
        }
        mDailyTimestampFullTexts = generateTimestampDayOfWeekTexts(
                mContext, batteryLevelData.getDailyBatteryLevels().getTimestamps(),
                /* isAbbreviation= */ false);
        mDailyViewModel = new BatteryChartViewModel(
                batteryLevelData.getDailyBatteryLevels().getLevels(),
                generateTimestampDayOfWeekTexts(
                        mContext, batteryLevelData.getDailyBatteryLevels().getTimestamps(),
                        /* isAbbreviation= */ true),
                BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS);
                batteryLevelData.getDailyBatteryLevels().getTimestamps(),
                BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS,
                mDailyChartLabelTextGenerator);
        mHourlyViewModels = new ArrayList<>();
        for (BatteryLevelData.PeriodBatteryLevelData hourlyBatteryLevelsPerDay :
                batteryLevelData.getHourlyBatteryLevelsPerDay()) {
            mHourlyViewModels.add(new BatteryChartViewModel(
                    hourlyBatteryLevelsPerDay.getLevels(),
                    generateTimestampHourTexts(
                            mContext, hourlyBatteryLevelsPerDay.getTimestamps()),
                    BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
                    hourlyBatteryLevelsPerDay.getTimestamps(),
                    BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS,
                    mHourlyChartLabelTextGenerator));
        }
        refreshUi();
    }
@@ -334,6 +334,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
            mDailyChartIndex = trapezoidIndex;
            mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
            refreshUi();
            requestAccessibilityFocusForCategoryTitle(mDailyChartView);
            mMetricsFeatureProvider.action(
                    mPrefContext,
                    trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
@@ -349,6 +350,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
            Log.d(TAG, "onHourlyChartSelect:" + trapezoidIndex);
            mHourlyChartIndex = trapezoidIndex;
            refreshUi();
            requestAccessibilityFocusForCategoryTitle(mHourlyChartView);
            mMetricsFeatureProvider.action(
                    mPrefContext,
                    trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
@@ -532,6 +534,18 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
        }
    }

    private void requestAccessibilityFocusForCategoryTitle(View view) {
        if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
            return;
        }
        if (mCategoryTitleView == null) {
            mCategoryTitleView = view.getRootView().findViewById(com.android.internal.R.id.title);
        }
        if (mCategoryTitleView != null) {
            mCategoryTitleView.requestAccessibilityFocus();
        }
    }

    private String getSlotInformation(boolean isApp, String slotInformation) {
        // TODO: Updates the right slot information from daily and hourly chart selection.
        // Null means we show all information without a specific time slot.
@@ -548,8 +562,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll

    @VisibleForTesting
    String getSlotInformation() {
        if (mDailyTimestampFullTexts == null || mDailyViewModel == null
                || mHourlyViewModels == null) {
        if (mDailyViewModel == null || mHourlyViewModels == null) {
            // No data
            return null;
        }
@@ -557,17 +570,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
            return null;
        }

        final String selectedDayText = mDailyTimestampFullTexts.get(mDailyChartIndex);
        final String selectedDayText = mDailyViewModel.getFullText(mDailyChartIndex);
        if (mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
            return selectedDayText;
        }

        final String fromHourText = mHourlyViewModels.get(mDailyChartIndex).texts().get(
        final String selectedHourText = mHourlyViewModels.get(mDailyChartIndex).getFullText(
                mHourlyChartIndex);
        final String toHourText = mHourlyViewModels.get(mDailyChartIndex).texts().get(
                mHourlyChartIndex + 1);
        final String selectedHourText =
                String.format("%s%s%s", fromHourText, mIs24HourFormat ? "-" : " - ", toHourText);
        if (isBatteryLevelDataInOneDay()) {
            return selectedHourText;
        }
@@ -712,25 +721,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
                / DateUtils.HOUR_IN_MILLIS);
    }

    private static List<String> generateTimestampDayOfWeekTexts(@NonNull final Context context,
            @NonNull final List<Long> timestamps, final boolean isAbbreviation) {
        final ArrayList<String> texts = new ArrayList<>();
        for (Long timestamp : timestamps) {
            texts.add(ConvertUtils.utcToLocalTimeDayOfWeek(context, timestamp, isAbbreviation));
        }
        return texts;
    }

    private static List<String> generateTimestampHourTexts(
            @NonNull final Context context, @NonNull final List<Long> timestamps) {
        final boolean is24HourFormat = DateFormat.is24HourFormat(context);
        final ArrayList<String> texts = new ArrayList<>();
        for (Long timestamp : timestamps) {
            texts.add(ConvertUtils.utcToLocalTimeHour(context, timestamp, is24HourFormat));
        }
        return texts;
    }

    /** Used for {@link AppBatteryPreferenceController}. */
    public static List<BatteryDiffEntry> getAppBatteryUsageData(Context context) {
        final long start = System.currentTimeMillis();
@@ -776,4 +766,36 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
        }
        return null;
    }

    private final class DailyChartLabelTextGenerator implements
            BatteryChartViewModel.LabelTextGenerator {
        @Override
        public String generateText(List<Long> timestamps, int index) {
            return ConvertUtils.utcToLocalTimeDayOfWeek(mContext,
                    timestamps.get(index), /* isAbbreviation= */ true);
        }

        @Override
        public String generateFullText(List<Long> timestamps, int index) {
            return ConvertUtils.utcToLocalTimeDayOfWeek(mContext,
                    timestamps.get(index), /* isAbbreviation= */ false);
        }
    }

    private final class HourlyChartLabelTextGenerator implements
            BatteryChartViewModel.LabelTextGenerator {
        @Override
        public String generateText(List<Long> timestamps, int index) {
            return ConvertUtils.utcToLocalTimeHour(mContext, timestamps.get(index),
                    mIs24HourFormat);
        }

        @Override
        public String generateFullText(List<Long> timestamps, int index) {
            return index == timestamps.size() - 1
                    ? generateText(timestamps, index)
                    : String.format("%s%s%s", generateText(timestamps, index),
                            mIs24HourFormat ? "-" : " - ", generateText(timestamps, index + 1));
        }
    }
}
+130 −144

File changed.

Preview size limit exceeded, changes collapsed.

+54 −18
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.settings.fuelgauge.batteryusage;
import androidx.annotation.NonNull;
import androidx.core.util.Preconditions;

import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
@@ -38,34 +39,59 @@ class BatteryChartViewModel {
        CENTER_OF_TRAPEZOIDS,
    }

    interface LabelTextGenerator {
        /** Generate the label text. The text may be abbreviated to save space. */
        String generateText(List<Long> timestamps, int index);

        /** Generate the full text for accessibility. */
        String generateFullText(List<Long> timestamps, int index);
    }

    private final List<Integer> mLevels;
    private final List<String> mTexts;
    private final List<Long> mTimestamps;
    private final AxisLabelPosition mAxisLabelPosition;
    private final LabelTextGenerator mLabelTextGenerator;
    private final String[] mTexts;
    private final String[] mFullTexts;

    private int mSelectedIndex = SELECTED_INDEX_ALL;

    BatteryChartViewModel(
            @NonNull List<Integer> levels, @NonNull List<String> texts,
            @NonNull AxisLabelPosition axisLabelPosition) {
    BatteryChartViewModel(@NonNull List<Integer> levels, @NonNull List<Long> timestamps,
            @NonNull AxisLabelPosition axisLabelPosition,
            @NonNull LabelTextGenerator labelTextGenerator) {
        Preconditions.checkArgument(
                levels.size() == texts.size() && levels.size() >= MIN_LEVELS_DATA_SIZE,
                levels.size() == timestamps.size() && levels.size() >= MIN_LEVELS_DATA_SIZE,
                String.format(Locale.ENGLISH,
                        "Invalid BatteryChartViewModel levels.size: %d, texts.size: %d.",
                        levels.size(), texts.size()));
                        "Invalid BatteryChartViewModel levels.size: %d, timestamps.size: %d.",
                        levels.size(), timestamps.size()));
        mLevels = levels;
        mTexts = texts;
        mTimestamps = timestamps;
        mAxisLabelPosition = axisLabelPosition;
        mLabelTextGenerator = labelTextGenerator;
        mTexts = new String[size()];
        mFullTexts = new String[size()];
    }

    public int size() {
        return mLevels.size();
    }

    public List<Integer> levels() {
        return mLevels;
    public Integer getLevel(int index) {
        return mLevels.get(index);
    }

    public String getText(int index) {
        if (mTexts[index] == null) {
            mTexts[index] = mLabelTextGenerator.generateText(mTimestamps, index);
        }
        return mTexts[index];
    }

    public List<String> texts() {
        return mTexts;
    public String getFullText(int index) {
        if (mFullTexts[index] == null) {
            mFullTexts[index] = mLabelTextGenerator.generateFullText(mTimestamps, index);
        }
        return mFullTexts[index];
    }

    public AxisLabelPosition axisLabelPosition() {
@@ -82,7 +108,7 @@ class BatteryChartViewModel {

    @Override
    public int hashCode() {
        return Objects.hash(mLevels, mTexts, mSelectedIndex, mAxisLabelPosition);
        return Objects.hash(mLevels, mTimestamps, mSelectedIndex, mAxisLabelPosition);
    }

    @Override
@@ -94,16 +120,26 @@ class BatteryChartViewModel {
        }
        final BatteryChartViewModel batteryChartViewModel = (BatteryChartViewModel) other;
        return Objects.equals(mLevels, batteryChartViewModel.mLevels)
                && Objects.equals(mTexts, batteryChartViewModel.mTexts)
                && Objects.equals(mTimestamps, batteryChartViewModel.mTimestamps)
                && mAxisLabelPosition == batteryChartViewModel.mAxisLabelPosition
                && mSelectedIndex == batteryChartViewModel.mSelectedIndex;
    }

    @Override
    public String toString() {
        return String.format(Locale.ENGLISH,
                "levels: %s,\ntexts: %s,\naxisLabelPosition: %s, selectedIndex: %d",
                Objects.toString(mLevels), Objects.toString(mTexts), mAxisLabelPosition,
                mSelectedIndex);
        // Generate all the texts and full texts.
        for (int i = 0; i < size(); i++) {
            getText(i);
            getFullText(i);
        }

        return new StringBuilder()
                .append("levels: " + Objects.toString(mLevels))
                .append(", timestamps: " + Objects.toString(mTimestamps))
                .append(", texts: " + Arrays.toString(mTexts))
                .append(", fullTexts: " + Arrays.toString(mFullTexts))
                .append(", axisLabelPosition: " + mAxisLabelPosition)
                .append(", selectedIndex: " + mSelectedIndex)
                .toString();
    }
}
+55 −14
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.settings.fuelgauge.batteryusage;
import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
@@ -173,22 +174,33 @@ public final class BatteryChartPreferenceControllerTest {

    @Test
    public void setBatteryChartViewModel_6Hours() {
        reset(mHourlyChartView);
        mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));

        verify(mDailyChartView, atLeastOnce()).setVisibility(View.GONE);
        verify(mHourlyChartView, atLeastOnce()).setVisibility(View.VISIBLE);
        verify(mHourlyChartView).setViewModel(new BatteryChartViewModel(
        // Ignore fast refresh ui from the data processor callback.
        verify(mHourlyChartView, atLeast(0)).setViewModel(null);
        verify(mHourlyChartView, atLeastOnce()).setViewModel(new BatteryChartViewModel(
                List.of(100, 97, 95),
                List.of("8 AM", "10 AM", "12 PM"),
                BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
                List.of(1619251200000L /* 8 AM */,
                        1619258400000L /* 10 AM */,
                        1619265600000L /* 12 PM */),
                BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS,
                mBatteryChartPreferenceController.mHourlyChartLabelTextGenerator));
    }

    @Test
    public void setBatteryChartViewModel_60Hours() {
        BatteryChartViewModel expectedDailyViewModel = new BatteryChartViewModel(
                List.of(100, 83, 59, 41),
                List.of("Sat", "Sun", "Mon", "Mon"),
                BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS);
                // "Sat", "Sun", "Mon", "Mon"
                List.of(1619251200000L /* Sat */,
                        1619308800000L /* Sun */,
                        1619395200000L /* Mon */,
                        1619460000000L /* Mon */),
                BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS,
                mBatteryChartPreferenceController.mDailyChartLabelTextGenerator);

        mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));

@@ -208,9 +220,17 @@ public final class BatteryChartPreferenceControllerTest {
        verify(mDailyChartView).setViewModel(expectedDailyViewModel);
        verify(mHourlyChartView).setViewModel(new BatteryChartViewModel(
                List.of(100, 97, 95, 93, 91, 89, 87, 85, 83),
                List.of("8 AM", "10 AM", "12 PM", "2 PM", "4 PM", "6 PM", "8 PM", "10 PM",
                        "12 AM"),
                BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
                List.of(1619251200000L /* 8 AM */,
                        1619258400000L /* 10 AM */,
                        1619265600000L /* 12 PM */,
                        1619272800000L /* 2 PM */,
                        1619280000000L /* 4 PM */,
                        1619287200000L /* 6 PM */,
                        1619294400000L /* 8 PM */,
                        1619301600000L /* 10 PM */,
                        1619308800000L /* 12 AM */),
                BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS,
                mBatteryChartPreferenceController.mHourlyChartLabelTextGenerator));

        reset(mDailyChartView);
        reset(mHourlyChartView);
@@ -224,9 +244,21 @@ public final class BatteryChartPreferenceControllerTest {
        verify(mDailyChartView).setViewModel(expectedDailyViewModel);
        BatteryChartViewModel expectedHourlyViewModel = new BatteryChartViewModel(
                List.of(83, 81, 79, 77, 75, 73, 71, 69, 67, 65, 63, 61, 59),
                List.of("12 AM", "2 AM", "4 AM", "6 AM", "8 AM", "10 AM", "12 PM", "2 PM",
                        "4 PM", "6 PM", "8 PM", "10 PM", "12 AM"),
                BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS);
                List.of(1619308800000L /* 12 AM */,
                        1619316000000L /* 2 AM */,
                        1619323200000L /* 4 AM */,
                        1619330400000L /* 6 AM */,
                        1619337600000L /* 8 AM */,
                        1619344800000L /* 10 AM */,
                        1619352000000L /* 12 PM */,
                        1619359200000L /* 2 PM */,
                        1619366400000L /* 4 PM */,
                        1619373600000L /* 6 PM */,
                        1619380800000L /* 8 PM */,
                        1619388000000L /* 10 PM */,
                        1619395200000L /* 12 AM */),
                BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS,
                mBatteryChartPreferenceController.mHourlyChartLabelTextGenerator);
        expectedHourlyViewModel.setSelectedIndex(6);
        verify(mHourlyChartView).setViewModel(expectedHourlyViewModel);

@@ -243,9 +275,18 @@ public final class BatteryChartPreferenceControllerTest {
        verify(mDailyChartView).setViewModel(expectedDailyViewModel);
        verify(mHourlyChartView).setViewModel(new BatteryChartViewModel(
                List.of(59, 57, 55, 53, 51, 49, 47, 45, 43, 41),
                List.of("12 AM", "2 AM", "4 AM", "6 AM", "8 AM", "10 AM", "12 PM", "2 PM",
                        "4 PM", "6 PM"),
                BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
                List.of(1619395200000L /* 12 AM */,
                        1619402400000L /* 2 AM */,
                        1619409600000L /* 4 AM */,
                        1619416800000L /* 6 AM */,
                        1619424000000L /* 8 AM */,
                        1619431200000L /* 10 AM */,
                        1619438400000L /* 12 PM */,
                        1619445600000L /* 2 PM */,
                        1619452800000L /* 4 PM */,
                        1619460000000L /* 6 PM */),
                BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS,
                mBatteryChartPreferenceController.mHourlyChartLabelTextGenerator));
    }

    @Test
+2 −154

File changed.

Preview size limit exceeded, changes collapsed.