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