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 
40 /**
41  * Abstract class for a {@link Fragment} that implements the Home App's View interface.
42  *
43  * {@link HomeCardInterface.View} classes that extend HomeCardFragment will override the update
44  * methods of the types of CardContent that they support. Each CardContent class corresponds to a
45  * layout shown on the card. The layout is a combination of xml files.
46  * {@link DescriptiveTextWithControlsView}: card_content_descriptive_text, card_content_button_trio
47  * {@link DescriptiveTextView}: card_content_descriptive_text, card_content_tap_for_more_text
48  * {@link TextBlockView}: card_content_text_block, card_content_tap_for_more_text
49  */
50 public class HomeCardFragment extends Fragment implements HomeCardInterface.View {
51 
52     private static final String TAG = "HomeFragment";
53     private HomeCardInterface.Presenter mPresenter;
54     private Size mSize;
55     private View mCardBackground;
56     private CrossfadeImageView mCardBackgroundImage;
57     private View mRootView;
58     private TextView mCardTitle;
59     private ImageView mCardIcon;
60 
61     // Views from card_content_text_block.xml
62     private View mTextBlockLayoutView;
63     private TextView mTextBlock;
64     private TextView mTextBlockTapForMore;
65 
66     // Views from card_content_descriptive_text_only.xml
67     private View mDescriptiveTextOnlyLayoutView;
68     private ImageView mDescriptiveTextOnlyOptionalImage;
69     private TextView mDescriptiveTextOnlyTitle;
70     private TextView mDescriptiveTextOnlySubtitle;
71     private TextView mDescriptiveTextOnlyTapForMore;
72 
73     // Views from card_content_descriptive_text_with_controls.xml
74     private View mDescriptiveTextWithControlsLayoutView;
75     private ImageView mDescriptiveTextWithControlsOptionalImage;
76     private TextView mDescriptiveTextWithControlsTitle;
77     private TextView mDescriptiveTextWithControlsSubtitle;
78     private View mControlBarView;
79     private ImageButton mControlBarLeftButton;
80     private ImageButton mControlBarCenterButton;
81     private ImageButton mControlBarRightButton;
82 
83     @Override
setPresenter(HomeCardInterface.Presenter presenter)84     public void setPresenter(HomeCardInterface.Presenter presenter) {
85         mPresenter = presenter;
86     }
87 
88     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)89     public View onCreateView(LayoutInflater inflater, ViewGroup container,
90             Bundle savedInstanceState) {
91         mRootView = inflater.inflate(R.layout.card_fragment, container, false);
92         mCardTitle = mRootView.findViewById(R.id.card_name);
93         mCardIcon = mRootView.findViewById(R.id.card_icon);
94         return mRootView;
95     }
96 
97     @Override
onViewCreated(View view, Bundle savedInstanceState)98     public void onViewCreated(View view, Bundle savedInstanceState) {
99         super.onViewCreated(view, savedInstanceState);
100         mPresenter.onViewCreated();
101         mRootView.setOnClickListener(v -> mPresenter.onViewClicked(v));
102     }
103 
104     @Override
onDestroy()105     public void onDestroy() {
106         super.onDestroy();
107         if (mPresenter != null) {
108             mPresenter.onViewDestroyed();
109         }
110         mSize = null;
111     }
112 
113     @Override
getFragment()114     public Fragment getFragment() {
115         return this;
116     }
117 
118     /**
119      * Returns the size of the card or null if the view hasn't yet been laid out
120      */
getCardSize()121     protected Size getCardSize() {
122         if (mSize == null && mRootView.isLaidOut()) {
123             mSize = new Size(mRootView.getWidth(), mRootView.getHeight());
124         }
125         return mSize;
126     }
127 
128     /**
129      * Removes the audio card from view
130      */
131     @Override
hideCard()132     public void hideCard() {
133         hideAllViews();
134         mRootView.setVisibility(View.GONE);
135     }
136 
137     /**
138      * Updates the card's header: name and icon of source app
139      */
140     @Override
updateHeaderView(CardHeader header)141     public void updateHeaderView(CardHeader header) {
142         requireActivity().runOnUiThread(() -> {
143             mRootView.setVisibility(View.VISIBLE);
144             mCardTitle.setText(header.getCardTitle());
145             mCardIcon.setImageDrawable(header.getCardIcon());
146         });
147     }
148 
149     @Override
updateContentView(CardContent content)150     public final void updateContentView(CardContent content) {
151         requireActivity().runOnUiThread(() -> {
152             hideAllViews();
153             updateContentViewInternal(content);
154         });
155     }
156 
157     /**
158      * Child classes can override this method for updating their specific types of card content
159      */
updateContentViewInternal(CardContent content)160     protected void updateContentViewInternal(CardContent content) {
161         switch (content.getType()) {
162             case DESCRIPTIVE_TEXT:
163                 DescriptiveTextView descriptiveTextContent = (DescriptiveTextView) content;
164                 updateDescriptiveTextOnlyView(descriptiveTextContent.getTitle(),
165                         descriptiveTextContent.getSubtitle(), descriptiveTextContent.getImage(),
166                         descriptiveTextContent.getFooter());
167                 break;
168             case DESCRIPTIVE_TEXT_WITH_CONTROLS:
169                 DescriptiveTextWithControlsView
170                         descriptiveTextWithControlsContent =
171                         (DescriptiveTextWithControlsView) content;
172                 updateDescriptiveTextWithControlsView(descriptiveTextWithControlsContent.getTitle(),
173                         descriptiveTextWithControlsContent.getSubtitle(),
174                         descriptiveTextWithControlsContent.getImage(),
175                         descriptiveTextWithControlsContent.getLeftControl(),
176                         descriptiveTextWithControlsContent.getCenterControl(),
177                         descriptiveTextWithControlsContent.getRightControl());
178                 break;
179             case TEXT_BLOCK:
180                 TextBlockView textBlockContent = (TextBlockView) content;
181                 updateTextBlock(textBlockContent.getText(), textBlockContent.getFooter());
182                 break;
183         }
184     }
185 
updateDescriptiveTextOnlyView(CharSequence primaryText, CharSequence secondaryText, Drawable optionalImage, CharSequence tapForMoreText)186     protected final void updateDescriptiveTextOnlyView(CharSequence primaryText,
187             CharSequence secondaryText, Drawable optionalImage, CharSequence tapForMoreText) {
188         getDescriptiveTextOnlyLayoutView().setVisibility(View.VISIBLE);
189         mDescriptiveTextOnlyTitle.setText(primaryText);
190         mDescriptiveTextOnlySubtitle.setText(secondaryText);
191         mDescriptiveTextOnlyOptionalImage.setImageDrawable(optionalImage);
192         mDescriptiveTextOnlyOptionalImage.setVisibility(
193                 optionalImage == null ? View.GONE : View.VISIBLE);
194         mDescriptiveTextOnlyTapForMore.setText(tapForMoreText);
195         mDescriptiveTextOnlyTapForMore.setVisibility(
196                 tapForMoreText == null ? View.GONE : View.VISIBLE);
197     }
198 
updateDescriptiveTextWithControlsView(CharSequence primaryText, CharSequence secondaryText, Drawable optionalImage, DescriptiveTextWithControlsView.Control leftButton, DescriptiveTextWithControlsView.Control centerButton, DescriptiveTextWithControlsView.Control rightButton)199     protected final void updateDescriptiveTextWithControlsView(CharSequence primaryText,
200             CharSequence secondaryText, Drawable optionalImage,
201             DescriptiveTextWithControlsView.Control leftButton,
202             DescriptiveTextWithControlsView.Control centerButton,
203             DescriptiveTextWithControlsView.Control rightButton) {
204         getDescriptiveTextWithControlsLayoutView().setVisibility(View.VISIBLE);
205         mDescriptiveTextWithControlsTitle.setText(primaryText);
206         mDescriptiveTextWithControlsSubtitle.setText(secondaryText);
207         mDescriptiveTextWithControlsOptionalImage.setImageDrawable(optionalImage);
208         mDescriptiveTextWithControlsOptionalImage.setVisibility(
209                 optionalImage == null ? View.GONE : View.VISIBLE);
210 
211         updateControlBarButton(leftButton, mControlBarLeftButton);
212         updateControlBarButton(centerButton, mControlBarCenterButton);
213         updateControlBarButton(rightButton, mControlBarRightButton);
214     }
215 
updateControlBarButton(DescriptiveTextWithControlsView.Control buttonContent, ImageButton buttonView)216     private void updateControlBarButton(DescriptiveTextWithControlsView.Control buttonContent,
217             ImageButton buttonView) {
218         if (buttonContent != null) {
219             buttonView.setImageDrawable(buttonContent.getIcon());
220             buttonView.setOnClickListener(buttonContent.getOnClickListener());
221             buttonView.setVisibility(View.VISIBLE);
222         } else {
223             buttonView.setVisibility(View.GONE);
224         }
225     }
226 
updateTextBlock(CharSequence mainText, CharSequence tapForMoreText)227     protected final void updateTextBlock(CharSequence mainText, CharSequence tapForMoreText) {
228         getTextBlockLayoutView().setVisibility(View.VISIBLE);
229         mTextBlock.setText(mainText);
230         mTextBlockTapForMore.setText(tapForMoreText);
231         mTextBlockTapForMore.setVisibility(tapForMoreText == null ? View.GONE : View.VISIBLE);
232     }
233 
hideAllViews()234     protected void hideAllViews() {
235         getTextBlockLayoutView().setVisibility(View.GONE);
236         getDescriptiveTextOnlyLayoutView().setVisibility(View.GONE);
237         getDescriptiveTextWithControlsLayoutView().setVisibility(View.GONE);
238     }
239 
getRootView()240     protected final View getRootView() {
241         return mRootView;
242     }
243 
getCardBackground()244     protected final View getCardBackground() {
245         if (mCardBackground == null) {
246             mCardBackground = getRootView().findViewById(R.id.card_background);
247         }
248         return mCardBackground;
249     }
250 
getCardBackgroundImage()251     protected final CrossfadeImageView getCardBackgroundImage() {
252         if (mCardBackgroundImage == null) {
253             mCardBackgroundImage = getRootView().findViewById(R.id.card_background_image);
254         }
255         return mCardBackgroundImage;
256     }
257 
getDescriptiveTextOnlyLayoutView()258     protected final View getDescriptiveTextOnlyLayoutView() {
259         if (mDescriptiveTextOnlyLayoutView == null) {
260             ViewStub stub = mRootView.findViewById(R.id.descriptive_text_layout);
261             mDescriptiveTextOnlyLayoutView = stub.inflate();
262             mDescriptiveTextOnlyTitle = mDescriptiveTextOnlyLayoutView.findViewById(
263                     R.id.primary_text);
264             mDescriptiveTextOnlySubtitle = mDescriptiveTextOnlyLayoutView.findViewById(
265                     R.id.secondary_text);
266             mDescriptiveTextOnlyOptionalImage = mDescriptiveTextOnlyLayoutView.findViewById(
267                     R.id.optional_image);
268             mDescriptiveTextOnlyTapForMore = mDescriptiveTextOnlyLayoutView.findViewById(
269                     R.id.tap_for_more_text);
270         }
271         return mDescriptiveTextOnlyLayoutView;
272     }
273 
getDescriptiveTextWithControlsLayoutView()274     protected final View getDescriptiveTextWithControlsLayoutView() {
275         if (mDescriptiveTextWithControlsLayoutView == null) {
276             ViewStub stub = mRootView.findViewById(R.id.descriptive_text_with_controls_layout);
277             mDescriptiveTextWithControlsLayoutView = stub.inflate();
278             mDescriptiveTextWithControlsTitle = mDescriptiveTextWithControlsLayoutView.findViewById(
279                     R.id.primary_text);
280             mDescriptiveTextWithControlsSubtitle =
281                     mDescriptiveTextWithControlsLayoutView.findViewById(R.id.secondary_text);
282             mDescriptiveTextWithControlsOptionalImage =
283                     mDescriptiveTextWithControlsLayoutView.findViewById(R.id.optional_image);
284             mControlBarView = mDescriptiveTextWithControlsLayoutView.findViewById(R.id.button_trio);
285             mControlBarLeftButton = mDescriptiveTextWithControlsLayoutView.findViewById(
286                     R.id.button_left);
287             mControlBarCenterButton = mDescriptiveTextWithControlsLayoutView.findViewById(
288                     R.id.button_center);
289             mControlBarRightButton = mDescriptiveTextWithControlsLayoutView.findViewById(
290                     R.id.button_right);
291         }
292         return mDescriptiveTextWithControlsLayoutView;
293     }
294 
getTextBlockLayoutView()295     private View getTextBlockLayoutView() {
296         if (mTextBlockLayoutView == null) {
297             ViewStub stub = mRootView.findViewById(R.id.text_block_layout);
298             mTextBlockLayoutView = stub.inflate();
299             mTextBlock = mTextBlockLayoutView.findViewById(R.id.text_block);
300             mTextBlockTapForMore = mTextBlockLayoutView.findViewById(R.id.tap_for_more_text);
301         }
302         return mTextBlockLayoutView;
303     }
304 }
305