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

Commit 00a97b4c authored by tmfang's avatar tmfang
Browse files

Allow each AppEntity to handle click events individually.

- Add a listener for each app entity view. So, they
can handle click events individually.

- Since the parameters of setAppEntity are increasing,
I create a builder class which save some information of
app entity.

- Clean up AppEntitiesHeaderController.

Test: visual, robotest
Bug: 121271387
Change-Id: I781773d566a87a30271b0d5b2dfc9fc1bd6b225e
parent 14a4f42b
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@
    android:layout_weight="1"
    android:layout_marginEnd="16dp"
    android:gravity="center"
    android:clickable="true"
    android:background="?android:attr/selectableItemBackground"
    android:orientation="vertical">

    <ImageView
+33 −52
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.settingslib.widget;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -32,23 +31,9 @@ import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;

/**
 * This is used to initialize view which was inflated
 * This class is used to initialize view which was inflated
 * from {@link R.xml.app_entities_header.xml}.
 *
 * <p>The view looks like below.
 *
 * <pre>
 * --------------------------------------------------------------
 * |                     Header title                           |
 * --------------------------------------------------------------
 * |    App1 icon       |   App2 icon        |   App3 icon      |
 * |    App1 title      |   App2 title       |   App3 title     |
 * |    App1 summary    |   App2 summary     |   App3 summary   |
 * |-------------------------------------------------------------
 * |                     Header details                         |
 * --------------------------------------------------------------
 * </pre>
 *
 * <p>How to use AppEntitiesHeaderController?
 *
 * <p>1. Add a {@link LayoutPreference} in layout XML file.
@@ -66,13 +51,20 @@ import androidx.annotation.VisibleForTesting;
 * View headerView = ((LayoutPreference) screen.findPreference("app_entities_header"))
 *         .findViewById(R.id.app_entities_header);
 *
 * final AppEntityInfo appEntityInfo = new AppEntityInfo.Builder()
 *         .setIcon(icon)
 *         .setTitle(title)
 *         .setSummary(summary)
 *         .setOnClickListener(view -> doSomething())
 *         .build();
 *
 * AppEntitiesHeaderController.newInstance(context, headerView)
 *         .setHeaderTitleRes(R.string.xxxxx)
 *         .setHeaderDetailsRes(R.string.xxxxx)
 *         .setHeaderDetailsClickListener(onClickListener)
 *         .setAppEntity(0, icon, "app title", "app summary")
 *         .setAppEntity(1, icon, "app title", "app summary")
 *         .setAppEntity(2, icon, "app title", "app summary")
 *         .setAppEntity(0, appEntityInfo)
 *         .setAppEntity(1, appEntityInfo)
 *         .setAppEntity(2, appEntityInfo)
 *         .apply();
 * </pre>
 */
@@ -81,13 +73,13 @@ public class AppEntitiesHeaderController {
    private static final String TAG = "AppEntitiesHeaderCtl";

    @VisibleForTesting
    static final int MAXIMUM_APPS = 3;
    public static final int MAXIMUM_APPS = 3;

    private final Context mContext;
    private final TextView mHeaderTitleView;
    private final Button mHeaderDetailsView;

    private final AppEntity[] mAppEntities;
    private final AppEntityInfo[] mAppEntityInfos;
    private final View[] mAppEntityViews;
    private final ImageView[] mAppIconViews;
    private final TextView[] mAppTitleViews;
@@ -113,7 +105,7 @@ public class AppEntitiesHeaderController {
        mHeaderTitleView = appEntitiesHeaderView.findViewById(R.id.header_title);
        mHeaderDetailsView = appEntitiesHeaderView.findViewById(R.id.header_details);

        mAppEntities = new AppEntity[MAXIMUM_APPS];
        mAppEntityInfos = new AppEntityInfo[MAXIMUM_APPS];
        mAppIconViews = new ImageView[MAXIMUM_APPS];
        mAppTitleViews = new TextView[MAXIMUM_APPS];
        mAppSummaryViews = new TextView[MAXIMUM_APPS];
@@ -163,15 +155,12 @@ public class AppEntitiesHeaderController {
     * Set an app entity at a specified position view.
     *
     * @param index         the index at which the specified view is to be inserted
     * @param icon the icon of app entity
     * @param titleRes the title of app entity
     * @param summaryRes the summary of app entity
     * @param appEntityInfo the information of an app entity
     * @return this {@code AppEntitiesHeaderController} object
     */
    public AppEntitiesHeaderController setAppEntity(int index, @NonNull Drawable icon,
            @Nullable CharSequence titleRes, @Nullable CharSequence summaryRes) {
        final AppEntity appEntity = new AppEntity(icon, titleRes, summaryRes);
        mAppEntities[index] = appEntity;
    public AppEntitiesHeaderController setAppEntity(int index,
            @NonNull AppEntityInfo appEntityInfo) {
        mAppEntityInfos[index] = appEntityInfo;
        return this;
    }

@@ -182,7 +171,7 @@ public class AppEntitiesHeaderController {
     * @return this {@code AppEntitiesHeaderController} object
     */
    public AppEntitiesHeaderController removeAppEntity(int index) {
        mAppEntities[index] = null;
        mAppEntityInfos[index] = null;
        return this;
    }

@@ -237,31 +226,23 @@ public class AppEntitiesHeaderController {
    }

    private void bindAppEntityView(int index) {
        final AppEntity appEntity = mAppEntities[index];
        mAppEntityViews[index].setVisibility(appEntity != null ? View.VISIBLE : View.GONE);
        final AppEntityInfo appEntityInfo = mAppEntityInfos[index];
        mAppEntityViews[index].setVisibility(appEntityInfo != null ? View.VISIBLE : View.GONE);

        if (appEntity != null) {
            mAppIconViews[index].setImageDrawable(appEntity.icon);
        if (appEntityInfo != null) {
            mAppEntityViews[index].setOnClickListener(appEntityInfo.getClickListener());

            mAppIconViews[index].setImageDrawable(appEntityInfo.getIcon());

            final CharSequence title = appEntityInfo.getTitle();
            mAppTitleViews[index].setVisibility(
                    TextUtils.isEmpty(appEntity.title) ? View.INVISIBLE : View.VISIBLE);
            mAppTitleViews[index].setText(appEntity.title);
                    TextUtils.isEmpty(title) ? View.INVISIBLE : View.VISIBLE);
            mAppTitleViews[index].setText(title);

            final CharSequence summary = appEntityInfo.getSummary();
            mAppSummaryViews[index].setVisibility(
                    TextUtils.isEmpty(appEntity.summary) ? View.INVISIBLE : View.VISIBLE);
            mAppSummaryViews[index].setText(appEntity.summary);
        }
    }

    private static class AppEntity {
        public final Drawable icon;
        public final CharSequence title;
        public final CharSequence summary;

        AppEntity(Drawable appIcon, CharSequence appTitle, CharSequence appSummary) {
            icon = appIcon;
            title = appTitle;
            summary = appSummary;
                    TextUtils.isEmpty(summary) ? View.INVISIBLE : View.VISIBLE);
            mAppSummaryViews[index].setText(summary);
        }
    }
}
+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 android.graphics.drawable.Drawable;
import android.view.View;

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

/**
 * AppEntityInfo is responsible for storing app information shown in {@link R.xml.app_view.xml}.
 */
public class AppEntityInfo {

    private final Drawable mIcon;
    private final CharSequence mTitle;
    private final CharSequence mSummary;
    private final View.OnClickListener mClickListener;

    /**
     * Gets the drawable for the icon of app entity.
     *
     * @return the drawable for the icon of app entity.
     */
    public Drawable getIcon() {
        return mIcon;
    }

    /**
     * Gets the text for the title of app enitity.
     *
     * @return the text for the title of app enitity.
     */
    public CharSequence getTitle() {
        return mTitle;
    }

    /**
     * Gets the text for the summary of app enitity.
     *
     * @return the text for the summary of app enitity.
     */
    public CharSequence getSummary() {
        return mSummary;
    }

    /**
     * Gets the click listener for the app entity view.
     *
     * @return click listener for the app entity view.
     */
    public View.OnClickListener getClickListener() {
        return mClickListener;
    }

    private AppEntityInfo(Builder builder) {
        mIcon = builder.mIcon;
        mTitle = builder.mTitle;
        mSummary = builder.mSummary;
        mClickListener = builder.mClickListener;
    }

    /**
     * Builder class for {@link AppEntityInfo}
     */
    public static class Builder {

        private Drawable mIcon;
        private CharSequence mTitle;
        private CharSequence mSummary;
        private View.OnClickListener mClickListener;

        /**
         * Creates an instance of a {@link AppEntityInfo} based on the current builder settings.
         *
         * @return The {@link AppEntityInfo}.
         */
        public AppEntityInfo build() {
            return new AppEntityInfo(this);
        }

        /**
         * Sets the drawable for the icon.
         */
        public Builder setIcon(@NonNull Drawable icon) {
            mIcon = icon;
            return this;
        }

        /**
         * Sets the text for the title.
         */
        public Builder setTitle(@Nullable CharSequence title) {
            mTitle = title;
            return this;
        }

        /**
         * Sets the text for the summary.
         */
        public Builder setSummary(@Nullable CharSequence summary) {
            mSummary = summary;
            return this;
        }

        /**
         * Sets the click listener for app entity view.
         */
        public Builder setOnClickListener(@Nullable View.OnClickListener clickListener) {
            mClickListener = clickListener;
            return this;
        }
    }
}
+21 −14
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.settingslib.widget;
import static com.google.common.truth.Truth.assertThat;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
@@ -43,18 +42,24 @@ public class AppEntitiesHeaderControllerTest {
    public final ExpectedException thrown = ExpectedException.none();

    private Context mContext;
    private Drawable mIcon;
    private View mAppEntitiesHeaderView;
    private AppEntitiesHeaderController mController;
    private AppEntityInfo mAppEntityInfo;

    @Before
    public void setUp() {
        mContext = RuntimeEnvironment.application;
        mAppEntitiesHeaderView = LayoutInflater.from(mContext).inflate(
                R.layout.app_entities_header, null /* root */);
        mIcon = mContext.getDrawable(R.drawable.ic_menu);
        mController = AppEntitiesHeaderController.newInstance(mContext,
                mAppEntitiesHeaderView);
        mAppEntityInfo = new AppEntityInfo.Builder()
                .setIcon(mContext.getDrawable(R.drawable.ic_menu))
                .setTitle(TITLE)
                .setSummary(SUMMARY)
                .setOnClickListener(v -> {
                })
                .build();
    }

    @Test
@@ -91,26 +96,26 @@ public class AppEntitiesHeaderControllerTest {
    public void setAppEntity_indexLessThanZero_shouldThrowArrayIndexOutOfBoundsException() {
        thrown.expect(ArrayIndexOutOfBoundsException.class);

        mController.setAppEntity(-1, mIcon, TITLE, SUMMARY);
        mController.setAppEntity(-1, mAppEntityInfo);
    }

    @Test
    public void asetAppEntity_indexGreaterThanMaximum_shouldThrowArrayIndexOutOfBoundsException() {
        thrown.expect(ArrayIndexOutOfBoundsException.class);

        mController.setAppEntity(AppEntitiesHeaderController.MAXIMUM_APPS + 1, mIcon, TITLE,
                SUMMARY);
        mController.setAppEntity(AppEntitiesHeaderController.MAXIMUM_APPS + 1, mAppEntityInfo);
    }

    @Test
    public void setAppEntity_addAppToIndex0_shouldShowAppView1() {
        mController.setAppEntity(0, mIcon, TITLE, SUMMARY).apply();
        mController.setAppEntity(0, mAppEntityInfo).apply();
        final View app1View = mAppEntitiesHeaderView.findViewById(R.id.app1_view);
        final ImageView appIconView = app1View.findViewById(R.id.app_icon);
        final TextView appTitle = app1View.findViewById(R.id.app_title);
        final TextView appSummary = app1View.findViewById(R.id.app_summary);

        assertThat(app1View.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(app1View.hasOnClickListeners()).isTrue();
        assertThat(appIconView.getDrawable()).isNotNull();
        assertThat(appTitle.getText()).isEqualTo(TITLE);
        assertThat(appSummary.getText()).isEqualTo(SUMMARY);
@@ -118,13 +123,14 @@ public class AppEntitiesHeaderControllerTest {

    @Test
    public void setAppEntity_addAppToIndex1_shouldShowAppView2() {
        mController.setAppEntity(1, mIcon, TITLE, SUMMARY).apply();
        mController.setAppEntity(1, mAppEntityInfo).apply();
        final View app2View = mAppEntitiesHeaderView.findViewById(R.id.app2_view);
        final ImageView appIconView = app2View.findViewById(R.id.app_icon);
        final TextView appTitle = app2View.findViewById(R.id.app_title);
        final TextView appSummary = app2View.findViewById(R.id.app_summary);

        assertThat(app2View.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(app2View.hasOnClickListeners()).isTrue();
        assertThat(appIconView.getDrawable()).isNotNull();
        assertThat(appTitle.getText()).isEqualTo(TITLE);
        assertThat(appSummary.getText()).isEqualTo(SUMMARY);
@@ -132,13 +138,14 @@ public class AppEntitiesHeaderControllerTest {

    @Test
    public void setAppEntity_addAppToIndex2_shouldShowAppView3() {
        mController.setAppEntity(2, mIcon, TITLE, SUMMARY).apply();
        mController.setAppEntity(2, mAppEntityInfo).apply();
        final View app3View = mAppEntitiesHeaderView.findViewById(R.id.app3_view);
        final ImageView appIconView = app3View.findViewById(R.id.app_icon);
        final TextView appTitle = app3View.findViewById(R.id.app_title);
        final TextView appSummary = app3View.findViewById(R.id.app_summary);

        assertThat(app3View.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(app3View.hasOnClickListeners()).isTrue();
        assertThat(appIconView.getDrawable()).isNotNull();
        assertThat(appTitle.getText()).isEqualTo(TITLE);
        assertThat(appSummary.getText()).isEqualTo(SUMMARY);
@@ -146,8 +153,8 @@ public class AppEntitiesHeaderControllerTest {

    @Test
    public void removeAppEntity_removeIndex0_shouldNotShowAppView1() {
        mController.setAppEntity(0, mIcon, TITLE, SUMMARY)
                .setAppEntity(1, mIcon, TITLE, SUMMARY).apply();
        mController.setAppEntity(0, mAppEntityInfo)
                .setAppEntity(1, mAppEntityInfo).apply();
        final View app1View = mAppEntitiesHeaderView.findViewById(R.id.app1_view);
        final View app2View = mAppEntitiesHeaderView.findViewById(R.id.app2_view);

@@ -162,9 +169,9 @@ public class AppEntitiesHeaderControllerTest {

    @Test
    public void clearAllAppEntities_shouldNotShowAllAppViews() {
        mController.setAppEntity(0, mIcon, TITLE, SUMMARY)
                .setAppEntity(1, mIcon, TITLE, SUMMARY)
                .setAppEntity(2, mIcon, TITLE, SUMMARY).apply();
        mController.setAppEntity(0, mAppEntityInfo)
                .setAppEntity(1, mAppEntityInfo)
                .setAppEntity(2, mAppEntityInfo).apply();
        final View app1View = mAppEntitiesHeaderView.findViewById(R.id.app1_view);
        final View app2View = mAppEntitiesHeaderView.findViewById(R.id.app2_view);
        final View app3View = mAppEntitiesHeaderView.findViewById(R.id.app3_view);