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 LogUtil.i("MultimediaFragment.onCreateView", "show spam layout"); 111 return layoutInflater.inflate(R.layout.fragment_spam, viewGroup, false); 112 } 113 114 boolean hasImage = getImageUri() != null; 115 boolean hasSubject = !TextUtils.isEmpty(getSubject()); 116 boolean hasMap = getLocation() != null; 117 if (hasMap && MapsComponent.get(getContext()).getMaps().isAvailable()) { 118 if (hasImage) { 119 if (hasSubject) { 120 LogUtil.i("MultimediaFragment.onCreateView", "show text, image, location layout"); 121 return layoutInflater.inflate( 122 R.layout.fragment_composer_text_image_frag, viewGroup, false); 123 } else { 124 LogUtil.i("MultimediaFragment.onCreateView", "show image, location layout"); 125 return layoutInflater.inflate(R.layout.fragment_composer_image_frag, viewGroup, false); 126 } 127 } else if (hasSubject) { 128 LogUtil.i("MultimediaFragment.onCreateView", "show text, location layout"); 129 return layoutInflater.inflate(R.layout.fragment_composer_text_frag, viewGroup, false); 130 } else { 131 LogUtil.i("MultimediaFragment.onCreateView", "show location layout"); 132 return layoutInflater.inflate(R.layout.fragment_composer_frag, viewGroup, false); 133 } 134 } else if (hasImage) { 135 if (hasSubject) { 136 LogUtil.i("MultimediaFragment.onCreateView", "show text, image layout"); 137 return layoutInflater.inflate(R.layout.fragment_composer_text_image, viewGroup, false); 138 } else { 139 LogUtil.i("MultimediaFragment.onCreateView", "show image layout"); 140 return layoutInflater.inflate(R.layout.fragment_composer_image, viewGroup, false); 141 } 142 } else { 143 LogUtil.i("MultimediaFragment.onCreateView", "show text layout"); 144 return layoutInflater.inflate(R.layout.fragment_composer_text, viewGroup, false); 145 } 146 } 147 148 @Override onViewCreated(View view, @Nullable Bundle bundle)149 public void onViewCreated(View view, @Nullable Bundle bundle) { 150 super.onViewCreated(view, bundle); 151 View container = view.findViewById(R.id.answer_message_container); 152 if (container != null) { 153 container.setClipToOutline(true); 154 } 155 156 // If the call is spam and only has a subject, update the view to reflect that. 157 if (isSpam 158 && getLocation() == null 159 && getImageUri() == null 160 && !TextUtils.isEmpty(getSubject())) { 161 ((ImageView) view.findViewById(R.id.spam_image)) 162 .setImageResource(R.drawable.quantum_ic_message_white_24); 163 ((TextView) view.findViewById(R.id.spam_text)).setText(R.string.spam_message_text); 164 } 165 166 TextView messageText = view.findViewById(R.id.answer_message_text); 167 if (messageText != null) { 168 messageText.setText(getSubject()); 169 } 170 ImageView mainImage = view.findViewById(R.id.answer_message_image); 171 if (mainImage != null) { 172 Glide.with(this) 173 .load(getImageUri()) 174 .transition(DrawableTransitionOptions.withCrossFade()) 175 .listener( 176 new RequestListener<Drawable>() { 177 @Override 178 public boolean onLoadFailed( 179 @Nullable GlideException e, 180 Object model, 181 Target<Drawable> target, 182 boolean isFirstResource) { 183 view.findViewById(R.id.loading_spinner).setVisibility(View.GONE); 184 LogUtil.e("MultimediaFragment.onLoadFailed", null, e); 185 // TODO(a bug) handle error cases nicely 186 return false; // Let Glide handle the rest 187 } 188 189 @Override 190 public boolean onResourceReady( 191 Drawable drawable, 192 Object model, 193 Target<Drawable> target, 194 DataSource dataSource, 195 boolean isFirstResource) { 196 LogUtil.enterBlock("MultimediaFragment.onResourceReady"); 197 view.findViewById(R.id.loading_spinner).setVisibility(View.GONE); 198 return false; 199 } 200 }) 201 .into(mainImage); 202 mainImage.setClipToOutline(true); 203 } 204 FrameLayout fragmentHolder = view.findViewById(R.id.answer_message_frag); 205 if (fragmentHolder != null) { 206 fragmentHolder.setClipToOutline(true); 207 Fragment mapFragment = 208 MapsComponent.get(getContext()).getMaps().createStaticMapFragment(getLocation()); 209 getChildFragmentManager() 210 .beginTransaction() 211 .replace(R.id.answer_message_frag, mapFragment) 212 .commitNow(); 213 } 214 avatarImageView = view.findViewById(R.id.answer_message_avatar); 215 if (avatarImageView != null) { 216 avatarImageView.setVisibility(showAvatar ? View.VISIBLE : View.GONE); 217 } 218 219 Holder parent = FragmentUtils.getParent(this, Holder.class); 220 if (parent != null) { 221 parent.updateAvatar(this); 222 } 223 } 224 225 @Nullable 226 @Override getAvatarImageView()227 public ImageView getAvatarImageView() { 228 return avatarImageView; 229 } 230 231 @Override getAvatarSize()232 public int getAvatarSize() { 233 return getResources().getDimensionPixelSize(R.dimen.answer_message_avatar_size); 234 } 235 236 @Override shouldShowAnonymousAvatar()237 public boolean shouldShowAnonymousAvatar() { 238 return showAvatar; 239 } 240 241 @Nullable getSubject()242 public String getSubject() { 243 return getArguments().getString(ARG_SUBJECT); 244 } 245 246 @Nullable getImageUri()247 public Uri getImageUri() { 248 return getArguments().getParcelable(ARG_IMAGE); 249 } 250 251 @Nullable getLocation()252 public Location getLocation() { 253 return getArguments().getParcelable(ARG_LOCATION); 254 } 255 256 /** Interface for notifying the fragment parent of changes. */ 257 public interface Holder { updateAvatar(AvatarPresenter sessionDataScreen)258 void updateAvatar(AvatarPresenter sessionDataScreen); 259 } 260 } 261