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