1 /* 2 * Copyright (C) 2017 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.dialer.ui.contact; 18 19 import android.graphics.Bitmap; 20 import android.graphics.drawable.Drawable; 21 import android.net.Uri; 22 import android.os.Bundle; 23 import android.view.View; 24 25 import androidx.annotation.NonNull; 26 import androidx.annotation.Nullable; 27 import androidx.core.graphics.drawable.RoundedBitmapDrawable; 28 import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; 29 import androidx.lifecycle.LiveData; 30 import androidx.lifecycle.ViewModelProviders; 31 32 import com.android.car.apps.common.LetterTileDrawable; 33 import com.android.car.arch.common.FutureData; 34 import com.android.car.dialer.R; 35 import com.android.car.dialer.ui.common.DialerListBaseFragment; 36 import com.android.car.telephony.common.Contact; 37 import com.android.car.telephony.common.PhoneNumber; 38 import com.android.car.telephony.common.TelecomUtils; 39 import com.android.car.ui.toolbar.Toolbar; 40 41 import com.bumptech.glide.Glide; 42 import com.bumptech.glide.request.RequestOptions; 43 import com.bumptech.glide.request.target.SimpleTarget; 44 import com.bumptech.glide.request.transition.Transition; 45 46 /** 47 * A fragment that shows the name of the contact, the photo and all listed phone numbers. It is 48 * primarily used to respond to the results of search queries but supplyig it with the content:// 49 * uri of a contact should work too. 50 */ 51 public class ContactDetailsFragment extends DialerListBaseFragment implements 52 ContactDetailsAdapter.PhoneNumberPresenter { 53 private static final String TAG = "CD.ContactDetailsFragment"; 54 public static final String FRAGMENT_TAG = "CONTACT_DETAIL_FRAGMENT_TAG"; 55 56 // Key to load and save the contact entity instance. 57 private static final String KEY_CONTACT_ENTITY = "ContactEntity"; 58 59 private Contact mContact; 60 private LiveData<FutureData<Contact>> mContactDetailsLiveData; 61 private ContactDetailsViewModel mContactDetailsViewModel; 62 63 private boolean mShowActionBarView; 64 private boolean mShowActionBarAvatar; 65 66 /** 67 * Creates a new ContactDetailsFragment using a {@link Contact}. 68 */ newInstance(Contact contact)69 public static ContactDetailsFragment newInstance(Contact contact) { 70 ContactDetailsFragment fragment = new ContactDetailsFragment(); 71 Bundle args = new Bundle(); 72 args.putParcelable(KEY_CONTACT_ENTITY, contact); 73 fragment.setArguments(args); 74 return fragment; 75 } 76 77 @Override onCreate(Bundle savedInstanceState)78 public void onCreate(Bundle savedInstanceState) { 79 super.onCreate(savedInstanceState); 80 81 mContact = getArguments().getParcelable(KEY_CONTACT_ENTITY); 82 if (mContact == null && savedInstanceState != null) { 83 mContact = savedInstanceState.getParcelable(KEY_CONTACT_ENTITY); 84 } 85 mContactDetailsViewModel = ViewModelProviders.of(this).get( 86 ContactDetailsViewModel.class); 87 mContactDetailsLiveData = mContactDetailsViewModel.getContactDetails(mContact); 88 89 mShowActionBarView = getResources().getBoolean( 90 R.bool.config_show_contact_details_action_bar_view); 91 mShowActionBarAvatar = getResources().getBoolean( 92 R.bool.config_show_contact_details_action_bar_avatar); 93 } 94 95 @Override onViewCreated(View view, Bundle savedInstanceState)96 public void onViewCreated(View view, Bundle savedInstanceState) { 97 super.onViewCreated(view, savedInstanceState); 98 ContactDetailsAdapter contactDetailsAdapter = new ContactDetailsAdapter(getContext(), 99 mContact, this); 100 getRecyclerView().setAdapter(contactDetailsAdapter); 101 mContactDetailsLiveData.observe(this, contact -> { 102 if (contact.isLoading()) { 103 showLoading(); 104 } else { 105 onContactChanged(contact.getData()); 106 contactDetailsAdapter.setContact(contact.getData()); 107 showContent(); 108 } 109 }); 110 } 111 onContactChanged(Contact contact)112 private void onContactChanged(Contact contact) { 113 getArguments().clear(); 114 Toolbar toolbar = getActivity().findViewById(R.id.car_ui_toolbar); 115 // Null check to have unit tests to pass. 116 if (toolbar == null) { 117 return; 118 } 119 toolbar.setTitle(null); 120 toolbar.setLogo(null); 121 if (mShowActionBarView) { 122 toolbar.setTitle(contact == null ? getString(R.string.error_contact_deleted) 123 : contact.getDisplayName()); 124 if (mShowActionBarAvatar) { 125 int avatarSize = getResources().getDimensionPixelSize( 126 R.dimen.contact_details_action_bar_avatar_size); 127 LetterTileDrawable letterTile = TelecomUtils.createLetterTile(getContext(), 128 contact == null ? null : contact.getInitials(), 129 contact == null ? null : contact.getDisplayName()); 130 Uri avatarUri = contact == null ? null : contact.getAvatarUri(); 131 Glide.with(this) 132 .asBitmap() 133 .load(avatarUri) 134 .apply(new RequestOptions().override(avatarSize).error(letterTile)) 135 .into(new SimpleTarget<Bitmap>() { 136 @Override 137 public void onResourceReady(Bitmap bitmap, 138 Transition<? super Bitmap> transition) { 139 RoundedBitmapDrawable roundedBitmapDrawable = createFromBitmap( 140 bitmap, avatarSize); 141 toolbar.setLogo(roundedBitmapDrawable); 142 } 143 144 @Override 145 public void onLoadFailed(@Nullable Drawable errorDrawable) { 146 RoundedBitmapDrawable roundedBitmapDrawable = createFromLetterTile( 147 letterTile, avatarSize); 148 toolbar.setLogo(roundedBitmapDrawable); 149 } 150 }); 151 } 152 } 153 } 154 155 @Override setupToolbar(@onNull Toolbar toolbar)156 protected void setupToolbar(@NonNull Toolbar toolbar) { 157 toolbar.setState(getToolbarState()); 158 toolbar.setMenuItems(null); 159 160 // Remove the tool bar background if we don't show the action bar view. 161 if (!mShowActionBarView) { 162 setShowToolbarBackground(false); 163 } 164 165 setToolbarHeight(toolbar); 166 } 167 168 @Override onToolbarHeightChange(int toolbarHeight)169 public void onToolbarHeightChange(int toolbarHeight) { 170 if (mShowActionBarView) { 171 super.onToolbarHeightChange(toolbarHeight); 172 } else { 173 getRecyclerView().setScrollBarPadding(toolbarHeight, 0); 174 } 175 } 176 177 @Override getToolbarState()178 protected Toolbar.State getToolbarState() { 179 return Toolbar.State.SUBPAGE; 180 } 181 182 @Override onSaveInstanceState(Bundle outState)183 public void onSaveInstanceState(Bundle outState) { 184 super.onSaveInstanceState(outState); 185 outState.putParcelable(KEY_CONTACT_ENTITY, mContact); 186 } 187 188 @Override onClick(Contact contact, PhoneNumber phoneNumber)189 public void onClick(Contact contact, PhoneNumber phoneNumber) { 190 boolean isFavorite = phoneNumber.isFavorite(); 191 if (isFavorite) { 192 mContactDetailsViewModel.removeFromFavorite(contact, phoneNumber); 193 } else { 194 mContactDetailsViewModel.addToFavorite(contact, phoneNumber); 195 } 196 } 197 createFromLetterTile(LetterTileDrawable letterTileDrawable, int avatarSize)198 private RoundedBitmapDrawable createFromLetterTile(LetterTileDrawable letterTileDrawable, 199 int avatarSize) { 200 return createFromBitmap(letterTileDrawable.toBitmap(avatarSize), avatarSize); 201 } 202 createFromBitmap(Bitmap bitmap, int avatarSize)203 private RoundedBitmapDrawable createFromBitmap(Bitmap bitmap, int avatarSize) { 204 RoundedBitmapDrawable roundedBitmapDrawable = RoundedBitmapDrawableFactory.create( 205 getResources(), bitmap); 206 float radiusPercent = getResources() 207 .getFloat(R.dimen.contact_avatar_corner_radius_percent); 208 float radius = avatarSize * radiusPercent; 209 roundedBitmapDrawable.setCornerRadius(radius); 210 return roundedBitmapDrawable; 211 } 212 } 213