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