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