• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.incallui.sessiondata;
18 
19 import android.graphics.drawable.Drawable;
20 import android.location.Location;
21 import android.net.Uri;
22 import android.os.Bundle;
23 import android.support.annotation.NonNull;
24 import android.support.annotation.Nullable;
25 import android.support.annotation.VisibleForTesting;
26 import android.support.v4.app.Fragment;
27 import android.text.TextUtils;
28 import android.view.LayoutInflater;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.widget.FrameLayout;
32 import android.widget.ImageView;
33 import android.widget.TextView;
34 import com.android.dialer.common.FragmentUtils;
35 import com.android.dialer.common.LogUtil;
36 import com.android.dialer.multimedia.MultimediaData;
37 import com.android.incallui.maps.MapsComponent;
38 import com.bumptech.glide.Glide;
39 import com.bumptech.glide.load.DataSource;
40 import com.bumptech.glide.load.engine.GlideException;
41 import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
42 import com.bumptech.glide.request.RequestListener;
43 import com.bumptech.glide.request.target.Target;
44 
45 /**
46  * Displays info from {@link MultimediaData MultimediaData}.
47  *
48  * <p>Currently displays image, location (as a map), and message that come bundled with
49  * MultimediaData when calling {@link #newInstance(MultimediaData, boolean, boolean, boolean)}.
50  */
51 public class MultimediaFragment extends Fragment implements AvatarPresenter {
52 
53   private static final String ARG_SUBJECT = "subject";
54   private static final String ARG_IMAGE = "image";
55   private static final String ARG_LOCATION = "location";
56   private static final String ARG_INTERACTIVE = "interactive";
57   private static final String ARG_SHOW_AVATAR = "show_avatar";
58   private static final String ARG_IS_SPAM = "is_spam";
59   private ImageView avatarImageView;
60 
61   private boolean showAvatar;
62   private boolean isSpam;
63 
newInstance( @onNull MultimediaData multimediaData, boolean isInteractive, boolean showAvatar, boolean isSpam)64   public static MultimediaFragment newInstance(
65       @NonNull MultimediaData multimediaData,
66       boolean isInteractive,
67       boolean showAvatar,
68       boolean isSpam) {
69     return newInstance(
70         multimediaData.getText(),
71         multimediaData.getImageUri(),
72         multimediaData.getLocation(),
73         isInteractive,
74         showAvatar,
75         isSpam);
76   }
77 
78   @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
newInstance( @ullable String subject, @Nullable Uri imageUri, @Nullable Location location, boolean isInteractive, boolean showAvatar, boolean isSpam)79   public static MultimediaFragment newInstance(
80       @Nullable String subject,
81       @Nullable Uri imageUri,
82       @Nullable Location location,
83       boolean isInteractive,
84       boolean showAvatar,
85       boolean isSpam) {
86     Bundle args = new Bundle();
87     args.putString(ARG_SUBJECT, subject);
88     args.putParcelable(ARG_IMAGE, imageUri);
89     args.putParcelable(ARG_LOCATION, location);
90     args.putBoolean(ARG_INTERACTIVE, isInteractive);
91     args.putBoolean(ARG_SHOW_AVATAR, showAvatar);
92     args.putBoolean(ARG_IS_SPAM, isSpam);
93     MultimediaFragment fragment = new MultimediaFragment();
94     fragment.setArguments(args);
95     return fragment;
96   }
97 
98   @Override
onCreate(@ullable Bundle bundle)99   public void onCreate(@Nullable Bundle bundle) {
100     super.onCreate(bundle);
101     showAvatar = getArguments().getBoolean(ARG_SHOW_AVATAR);
102     isSpam = getArguments().getBoolean(ARG_IS_SPAM);
103   }
104 
105   @Nullable
106   @Override
onCreateView( LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle)107   public View onCreateView(
108       LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
109     if (isSpam) {
110       return layoutInflater.inflate(R.layout.fragment_spam, viewGroup, false);
111     }
112 
113     boolean hasImage = getImageUri() != null;
114     boolean hasSubject = !TextUtils.isEmpty(getSubject());
115     boolean hasMap = getLocation() != null;
116     if (hasMap && MapsComponent.get(getContext()).getMaps().isAvailable()) {
117       if (hasImage) {
118         if (hasSubject) {
119           return layoutInflater.inflate(
120               R.layout.fragment_composer_text_image_frag, viewGroup, false);
121         } else {
122           return layoutInflater.inflate(R.layout.fragment_composer_image_frag, viewGroup, false);
123         }
124       } else if (hasSubject) {
125         return layoutInflater.inflate(R.layout.fragment_composer_text_frag, viewGroup, false);
126       } else {
127         return layoutInflater.inflate(R.layout.fragment_composer_frag, viewGroup, false);
128       }
129     } else if (hasImage) {
130       if (hasSubject) {
131         return layoutInflater.inflate(R.layout.fragment_composer_text_image, viewGroup, false);
132       } else {
133         return layoutInflater.inflate(R.layout.fragment_composer_image, viewGroup, false);
134       }
135     } else {
136       return layoutInflater.inflate(R.layout.fragment_composer_text, viewGroup, false);
137     }
138   }
139 
140   @Override
onViewCreated(View view, @Nullable Bundle bundle)141   public void onViewCreated(View view, @Nullable Bundle bundle) {
142     super.onViewCreated(view, bundle);
143     View container = view.findViewById(R.id.answer_message_container);
144     if (container != null) {
145       container.setClipToOutline(true);
146     }
147 
148     // If the call is spam and only has a subject, update the view to reflect that.
149     if (isSpam
150         && getLocation() == null
151         && getImageUri() == null
152         && !TextUtils.isEmpty(getSubject())) {
153       ((ImageView) view.findViewById(R.id.spam_image))
154           .setImageResource(R.drawable.quantum_ic_message_white_24);
155       ((TextView) view.findViewById(R.id.spam_text)).setText(R.string.spam_message_text);
156     }
157 
158     TextView messageText = (TextView) view.findViewById(R.id.answer_message_text);
159     if (messageText != null) {
160       messageText.setText(getSubject());
161     }
162     ImageView mainImage = (ImageView) view.findViewById(R.id.answer_message_image);
163     if (mainImage != null) {
164       Glide.with(this)
165           .load(getImageUri())
166           .transition(DrawableTransitionOptions.withCrossFade())
167           .listener(
168               new RequestListener<Drawable>() {
169                 @Override
170                 public boolean onLoadFailed(
171                     @Nullable GlideException e,
172                     Object model,
173                     Target<Drawable> target,
174                     boolean isFirstResource) {
175                   view.findViewById(R.id.loading_spinner).setVisibility(View.GONE);
176                   LogUtil.e("MultimediaFragment.onLoadFailed", null, e);
177                   // TODO(b/34720074) handle error cases nicely
178                   return false; // Let Glide handle the rest
179                 }
180 
181                 @Override
182                 public boolean onResourceReady(
183                     Drawable drawable,
184                     Object model,
185                     Target<Drawable> target,
186                     DataSource dataSource,
187                     boolean isFirstResource) {
188                   view.findViewById(R.id.loading_spinner).setVisibility(View.GONE);
189                   return false;
190                 }
191               })
192           .into(mainImage);
193       mainImage.setClipToOutline(true);
194     }
195     FrameLayout fragmentHolder = (FrameLayout) view.findViewById(R.id.answer_message_frag);
196     if (fragmentHolder != null) {
197       fragmentHolder.setClipToOutline(true);
198       Fragment mapFragment =
199           MapsComponent.get(getContext()).getMaps().createStaticMapFragment(getLocation());
200       getChildFragmentManager()
201           .beginTransaction()
202           .replace(R.id.answer_message_frag, mapFragment)
203           .commitNow();
204     }
205     avatarImageView = ((ImageView) view.findViewById(R.id.answer_message_avatar));
206     if (avatarImageView != null) {
207       avatarImageView.setVisibility(showAvatar ? View.VISIBLE : View.GONE);
208     }
209 
210     Holder parent = FragmentUtils.getParent(this, Holder.class);
211     if (parent != null) {
212       parent.updateAvatar(this);
213     }
214   }
215 
216   @Nullable
217   @Override
getAvatarImageView()218   public ImageView getAvatarImageView() {
219     return avatarImageView;
220   }
221 
222   @Override
getAvatarSize()223   public int getAvatarSize() {
224     return getResources().getDimensionPixelSize(R.dimen.answer_message_avatar_size);
225   }
226 
227   @Override
shouldShowAnonymousAvatar()228   public boolean shouldShowAnonymousAvatar() {
229     return showAvatar;
230   }
231 
232   @Nullable
getSubject()233   public String getSubject() {
234     return getArguments().getString(ARG_SUBJECT);
235   }
236 
237   @Nullable
getImageUri()238   public Uri getImageUri() {
239     return getArguments().getParcelable(ARG_IMAGE);
240   }
241 
242   @Nullable
getLocation()243   public Location getLocation() {
244     return getArguments().getParcelable(ARG_LOCATION);
245   }
246 
247   /** Interface for notifying the fragment parent of changes. */
248   public interface Holder {
updateAvatar(AvatarPresenter sessionDataScreen)249     void updateAvatar(AvatarPresenter sessionDataScreen);
250   }
251 }
252