• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.carlauncher.homescreen;
18 
19 import android.graphics.drawable.Drawable;
20 import android.os.Bundle;
21 import android.util.Size;
22 import android.view.LayoutInflater;
23 import android.view.View;
24 import android.view.ViewGroup;
25 import android.view.ViewStub;
26 import android.widget.ImageButton;
27 import android.widget.ImageView;
28 import android.widget.TextView;
29 
30 import androidx.fragment.app.Fragment;
31 
32 import com.android.car.apps.common.CrossfadeImageView;
33 import com.android.car.carlauncher.R;
34 import com.android.car.carlauncher.homescreen.ui.CardContent;
35 import com.android.car.carlauncher.homescreen.ui.CardHeader;
36 import com.android.car.carlauncher.homescreen.ui.DescriptiveTextView;
37 import com.android.car.carlauncher.homescreen.ui.DescriptiveTextWithControlsView;
38 import com.android.car.carlauncher.homescreen.ui.TextBlockView;
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.internal.util.ArrayUtils;
41 
42 /**
43  * Abstract class for a {@link Fragment} that implements the Home App's View interface.
44  *
45  * {@link HomeCardInterface.View} classes that extend HomeCardFragment will override the update
46  * methods of the types of CardContent that they support. Each CardContent class corresponds to a
47  * layout shown on the card. The layout is a combination of xml files.
48  * {@link DescriptiveTextWithControlsView}: card_content_descriptive_text, card_content_button_trio
49  * {@link DescriptiveTextView}: card_content_descriptive_text, card_content_tap_for_more_text
50  * {@link TextBlockView}: card_content_text_block, card_content_tap_for_more_text
51  */
52 public class HomeCardFragment extends Fragment implements HomeCardInterface.View {
53 
54     private static final String TAG = "HomeFragment";
55     private HomeCardInterface.Presenter mPresenter;
56     private Size mSize;
57     private View mCardBackground;
58     private CrossfadeImageView mCardBackgroundImage;
59     private View mRootView;
60     private TextView mCardTitle;
61     private ImageView mCardIcon;
62 
63     // Views from card_content_text_block.xml
64     private View mTextBlockLayoutView;
65     private TextView mTextBlock;
66     private TextView mTextBlockTapForMore;
67 
68     // Views from card_content_descriptive_text_only.xml
69     private View mDescriptiveTextOnlyLayoutView;
70     private ImageView mDescriptiveTextOnlyOptionalImage;
71     private TextView mDescriptiveTextOnlyTitle;
72     private TextView mDescriptiveTextOnlySubtitle;
73     private TextView mDescriptiveTextOnlyTapForMore;
74 
75     // Views from card_content_descriptive_text_with_controls.xml
76     private View mDescriptiveTextWithControlsLayoutView;
77     private ImageView mDescriptiveTextWithControlsOptionalImage;
78     private TextView mDescriptiveTextWithControlsTitle;
79     private TextView mDescriptiveTextWithControlsSubtitle;
80     private View mControlBarView;
81     private ImageButton mControlBarLeftButton;
82     private ImageButton mControlBarCenterButton;
83     private ImageButton mControlBarRightButton;
84 
85     @Override
setPresenter(HomeCardInterface.Presenter presenter)86     public void setPresenter(HomeCardInterface.Presenter presenter) {
87         mPresenter = presenter;
88     }
89 
90     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)91     public View onCreateView(LayoutInflater inflater, ViewGroup container,
92             Bundle savedInstanceState) {
93         mRootView = inflater.inflate(R.layout.card_fragment, container, false);
94         mCardTitle = mRootView.findViewById(R.id.card_name);
95         mCardIcon = mRootView.findViewById(R.id.card_icon);
96         return mRootView;
97     }
98 
99     @Override
onViewCreated(View view, Bundle savedInstanceState)100     public void onViewCreated(View view, Bundle savedInstanceState) {
101         super.onViewCreated(view, savedInstanceState);
102         mPresenter.onViewCreated();
103         mRootView.setOnClickListener(v -> mPresenter.onViewClicked(v));
104     }
105 
106     @Override
onDestroy()107     public void onDestroy() {
108         super.onDestroy();
109         if (mPresenter != null) {
110             mPresenter.onViewDestroyed();
111         }
112         mSize = null;
113     }
114 
115     @Override
getFragment()116     public Fragment getFragment() {
117         return this;
118     }
119 
120     /**
121      * Returns the size of the card or null if the view hasn't yet been laid out
122      */
getCardSize()123     protected Size getCardSize() {
124         if (mSize == null && mRootView.isLaidOut()) {
125             mSize = new Size(mRootView.getWidth(), mRootView.getHeight());
126         }
127         return mSize;
128     }
129 
130     /**
131      * Removes the audio card from view
132      */
133     @Override
hideCard()134     public void hideCard() {
135         hideAllViews();
136         mRootView.setVisibility(View.GONE);
137     }
138 
139     /**
140      * Updates the card's header: name and icon of source app
141      */
142     @Override
updateHeaderView(CardHeader header)143     public void updateHeaderView(CardHeader header) {
144         requireActivity().runOnUiThread(() -> {
145             mRootView.setVisibility(View.VISIBLE);
146             mCardTitle.setText(header.getCardTitle());
147             mCardIcon.setImageDrawable(header.getCardIcon());
148         });
149     }
150 
151     @Override
updateContentView(CardContent content)152     public final void updateContentView(CardContent content) {
153         requireActivity().runOnUiThread(() -> {
154             hideAllViews();
155             updateContentViewInternal(content);
156         });
157     }
158 
159     /**
160      * Child classes can override this method for updating their specific types of card content
161      */
updateContentViewInternal(CardContent content)162     protected void updateContentViewInternal(CardContent content) {
163         switch (content.getType()) {
164             case DESCRIPTIVE_TEXT:
165                 DescriptiveTextView descriptiveTextContent = (DescriptiveTextView) content;
166                 updateDescriptiveTextOnlyView(descriptiveTextContent.getTitle(),
167                         descriptiveTextContent.getSubtitle(), descriptiveTextContent.getImage(),
168                         descriptiveTextContent.getFooter());
169                 break;
170             case DESCRIPTIVE_TEXT_WITH_CONTROLS:
171                 DescriptiveTextWithControlsView
172                         descriptiveTextWithControlsContent =
173                         (DescriptiveTextWithControlsView) content;
174                 updateDescriptiveTextWithControlsView(descriptiveTextWithControlsContent.getTitle(),
175                         descriptiveTextWithControlsContent.getSubtitle(),
176                         descriptiveTextWithControlsContent.getImage(),
177                         descriptiveTextWithControlsContent.getLeftControl(),
178                         descriptiveTextWithControlsContent.getCenterControl(),
179                         descriptiveTextWithControlsContent.getRightControl());
180                 break;
181             case TEXT_BLOCK:
182                 TextBlockView textBlockContent = (TextBlockView) content;
183                 updateTextBlock(textBlockContent.getText(), textBlockContent.getFooter());
184                 break;
185         }
186     }
187 
updateDescriptiveTextOnlyView(CharSequence primaryText, CharSequence secondaryText, Drawable optionalImage, CharSequence tapForMoreText)188     protected final void updateDescriptiveTextOnlyView(CharSequence primaryText,
189             CharSequence secondaryText, Drawable optionalImage, CharSequence tapForMoreText) {
190         getDescriptiveTextOnlyLayoutView().setVisibility(View.VISIBLE);
191         mDescriptiveTextOnlyTitle.setText(primaryText);
192         mDescriptiveTextOnlySubtitle.setText(secondaryText);
193         mDescriptiveTextOnlyOptionalImage.setImageDrawable(optionalImage);
194         mDescriptiveTextOnlyOptionalImage.setVisibility(
195                 optionalImage == null ? View.GONE : View.VISIBLE);
196         mDescriptiveTextOnlyTapForMore.setText(tapForMoreText);
197         mDescriptiveTextOnlyTapForMore.setVisibility(
198                 tapForMoreText == null ? View.GONE : View.VISIBLE);
199     }
200 
updateDescriptiveTextWithControlsView(CharSequence primaryText, CharSequence secondaryText, Drawable optionalImage, DescriptiveTextWithControlsView.Control leftButton, DescriptiveTextWithControlsView.Control centerButton, DescriptiveTextWithControlsView.Control rightButton)201     protected final void updateDescriptiveTextWithControlsView(CharSequence primaryText,
202             CharSequence secondaryText, Drawable optionalImage,
203             DescriptiveTextWithControlsView.Control leftButton,
204             DescriptiveTextWithControlsView.Control centerButton,
205             DescriptiveTextWithControlsView.Control rightButton) {
206         getDescriptiveTextWithControlsLayoutView().setVisibility(View.VISIBLE);
207         mDescriptiveTextWithControlsTitle.setText(primaryText);
208         mDescriptiveTextWithControlsSubtitle.setText(secondaryText);
209         mDescriptiveTextWithControlsOptionalImage.setImageDrawable(optionalImage);
210         mDescriptiveTextWithControlsOptionalImage.setVisibility(
211                 optionalImage == null ? View.GONE : View.VISIBLE);
212 
213         updateControlBarButton(leftButton, mControlBarLeftButton);
214         updateControlBarButton(centerButton, mControlBarCenterButton);
215         updateControlBarButton(rightButton, mControlBarRightButton);
216     }
217 
updateControlBarButton(DescriptiveTextWithControlsView.Control buttonContent, ImageButton buttonView)218     private void updateControlBarButton(DescriptiveTextWithControlsView.Control buttonContent,
219             ImageButton buttonView) {
220         if (buttonContent != null) {
221             buttonView.setImageDrawable(buttonContent.getIcon());
222             if (buttonContent.getIcon() != null) {
223                 // update the button view according to icon's selected state
224                 buttonView.setSelected(
225                         ArrayUtils.contains(buttonContent.getIcon().getState(),
226                                 android.R.attr.state_selected)
227                 );
228             }
229             buttonView.setOnClickListener(buttonContent.getOnClickListener());
230             buttonView.setVisibility(View.VISIBLE);
231         } else {
232             buttonView.setVisibility(View.GONE);
233         }
234     }
235 
updateTextBlock(CharSequence mainText, CharSequence tapForMoreText)236     protected final void updateTextBlock(CharSequence mainText, CharSequence tapForMoreText) {
237         getTextBlockLayoutView().setVisibility(View.VISIBLE);
238         mTextBlock.setText(mainText);
239         mTextBlockTapForMore.setText(tapForMoreText);
240         mTextBlockTapForMore.setVisibility(tapForMoreText == null ? View.GONE : View.VISIBLE);
241     }
242 
hideAllViews()243     protected void hideAllViews() {
244         getTextBlockLayoutView().setVisibility(View.GONE);
245         getDescriptiveTextOnlyLayoutView().setVisibility(View.GONE);
246         getDescriptiveTextWithControlsLayoutView().setVisibility(View.GONE);
247     }
248 
getRootView()249     protected final View getRootView() {
250         return mRootView;
251     }
252 
getCardBackground()253     protected final View getCardBackground() {
254         if (mCardBackground == null) {
255             mCardBackground = getRootView().findViewById(R.id.card_background);
256         }
257         return mCardBackground;
258     }
259 
getCardBackgroundImage()260     protected final CrossfadeImageView getCardBackgroundImage() {
261         if (mCardBackgroundImage == null) {
262             mCardBackgroundImage = getRootView().findViewById(R.id.card_background_image);
263         }
264         return mCardBackgroundImage;
265     }
266 
getDescriptiveTextOnlyLayoutView()267     protected final View getDescriptiveTextOnlyLayoutView() {
268         if (mDescriptiveTextOnlyLayoutView == null) {
269             ViewStub stub = mRootView.findViewById(R.id.descriptive_text_layout);
270             mDescriptiveTextOnlyLayoutView = stub.inflate();
271             mDescriptiveTextOnlyTitle = mDescriptiveTextOnlyLayoutView.findViewById(
272                     R.id.primary_text);
273             mDescriptiveTextOnlySubtitle = mDescriptiveTextOnlyLayoutView.findViewById(
274                     R.id.secondary_text);
275             mDescriptiveTextOnlyOptionalImage = mDescriptiveTextOnlyLayoutView.findViewById(
276                     R.id.optional_image);
277             mDescriptiveTextOnlyTapForMore = mDescriptiveTextOnlyLayoutView.findViewById(
278                     R.id.tap_for_more_text);
279         }
280         return mDescriptiveTextOnlyLayoutView;
281     }
282 
getDescriptiveTextWithControlsLayoutView()283     protected final View getDescriptiveTextWithControlsLayoutView() {
284         if (mDescriptiveTextWithControlsLayoutView == null) {
285             ViewStub stub = mRootView.findViewById(R.id.descriptive_text_with_controls_layout);
286             mDescriptiveTextWithControlsLayoutView = stub.inflate();
287             mDescriptiveTextWithControlsTitle = mDescriptiveTextWithControlsLayoutView.findViewById(
288                     R.id.primary_text);
289             mDescriptiveTextWithControlsSubtitle =
290                     mDescriptiveTextWithControlsLayoutView.findViewById(R.id.secondary_text);
291             mDescriptiveTextWithControlsOptionalImage =
292                     mDescriptiveTextWithControlsLayoutView.findViewById(R.id.optional_image);
293             mControlBarView = mDescriptiveTextWithControlsLayoutView.findViewById(R.id.button_trio);
294             mControlBarLeftButton = mDescriptiveTextWithControlsLayoutView.findViewById(
295                     R.id.button_left);
296             mControlBarCenterButton = mDescriptiveTextWithControlsLayoutView.findViewById(
297                     R.id.button_center);
298             mControlBarRightButton = mDescriptiveTextWithControlsLayoutView.findViewById(
299                     R.id.button_right);
300         }
301         return mDescriptiveTextWithControlsLayoutView;
302     }
303 
getTextBlockLayoutView()304     private View getTextBlockLayoutView() {
305         if (mTextBlockLayoutView == null) {
306             ViewStub stub = mRootView.findViewById(R.id.text_block_layout);
307             mTextBlockLayoutView = stub.inflate();
308             mTextBlock = mTextBlockLayoutView.findViewById(R.id.text_block);
309             mTextBlockTapForMore = mTextBlockLayoutView.findViewById(R.id.tap_for_more_text);
310         }
311         return mTextBlockLayoutView;
312     }
313 
314     @VisibleForTesting
setControlBarLeftButton(ImageButton controlBarLeftButton)315     void setControlBarLeftButton(ImageButton controlBarLeftButton) {
316         mControlBarLeftButton = controlBarLeftButton;
317     }
318 }
319