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
17package com.android.incallui.contactgrid;
18
19import android.content.Context;
20import android.support.annotation.Nullable;
21import android.telephony.PhoneNumberUtils;
22import android.text.BidiFormatter;
23import android.text.TextDirectionHeuristics;
24import android.text.TextUtils;
25import com.android.incallui.call.DialerCall.State;
26import com.android.incallui.incall.protocol.PrimaryCallState;
27import com.android.incallui.incall.protocol.PrimaryInfo;
28
29/**
30 * Gets the content of the bottom row. For example:
31 *
32 * <ul>
33 *   <li>Mobile +1 (650) 253-0000
34 *   <li>[HD attempting icon]/[HD icon] 00:15
35 *   <li>Call ended
36 *   <li>Hanging up
37 * </ul>
38 */
39public class BottomRow {
40
41  /** Content of the bottom row. */
42  public static class Info {
43
44    @Nullable public final CharSequence label;
45    public final boolean isTimerVisible;
46    public final boolean isWorkIconVisible;
47    public final boolean isHdAttemptingIconVisible;
48    public final boolean isHdIconVisible;
49    public final boolean isForwardIconVisible;
50    public final boolean isSpamIconVisible;
51    public final boolean shouldPopulateAccessibilityEvent;
52
53    public Info(
54        @Nullable CharSequence label,
55        boolean isTimerVisible,
56        boolean isWorkIconVisible,
57        boolean isHdAttemptingIconVisible,
58        boolean isHdIconVisible,
59        boolean isForwardIconVisible,
60        boolean isSpamIconVisible,
61        boolean shouldPopulateAccessibilityEvent) {
62      this.label = label;
63      this.isTimerVisible = isTimerVisible;
64      this.isWorkIconVisible = isWorkIconVisible;
65      this.isHdAttemptingIconVisible = isHdAttemptingIconVisible;
66      this.isHdIconVisible = isHdIconVisible;
67      this.isForwardIconVisible = isForwardIconVisible;
68      this.isSpamIconVisible = isSpamIconVisible;
69      this.shouldPopulateAccessibilityEvent = shouldPopulateAccessibilityEvent;
70    }
71  }
72
73  private BottomRow() {}
74
75  public static Info getInfo(Context context, PrimaryCallState state, PrimaryInfo primaryInfo) {
76    CharSequence label;
77    boolean isTimerVisible = state.state == State.ACTIVE;
78    boolean isForwardIconVisible = state.isForwardedNumber;
79    boolean isWorkIconVisible = state.isWorkCall;
80    boolean isHdIconVisible = state.isHdAudioCall && !isForwardIconVisible;
81    boolean isHdAttemptingIconVisible = state.isHdAttempting;
82    boolean isSpamIconVisible = false;
83    boolean shouldPopulateAccessibilityEvent = true;
84
85    if (isIncoming(state) && primaryInfo.isSpam) {
86      label = context.getString(R.string.contact_grid_incoming_suspected_spam);
87      isSpamIconVisible = true;
88      isHdIconVisible = false;
89    } else if (state.state == State.DISCONNECTING) {
90      // While in the DISCONNECTING state we display a "Hanging up" message in order to make the UI
91      // feel more responsive.  (In GSM it's normal to see a delay of a couple of seconds while
92      // negotiating the disconnect with the network, so the "Hanging up" state at least lets the
93      // user know that we're doing something.  This state is currently not used with CDMA.)
94      label = context.getString(R.string.incall_hanging_up);
95    } else if (state.state == State.DISCONNECTED) {
96      label = state.disconnectCause.getLabel();
97      if (TextUtils.isEmpty(label)) {
98        label = context.getString(R.string.incall_call_ended);
99      }
100    } else if (!TextUtils.isEmpty(state.callbackNumber)) {
101      // This is used for carriers like Project Fi to show the callback number for emergency calls.
102      label =
103          context.getString(
104              R.string.contact_grid_callback_number,
105              PhoneNumberUtils.formatNumber(state.callbackNumber));
106      isTimerVisible = false;
107    } else {
108      label = getLabelForPhoneNumber(primaryInfo);
109      shouldPopulateAccessibilityEvent = primaryInfo.nameIsNumber;
110    }
111
112    return new Info(
113        label,
114        isTimerVisible,
115        isWorkIconVisible,
116        isHdAttemptingIconVisible,
117        isHdIconVisible,
118        isForwardIconVisible,
119        isSpamIconVisible,
120        shouldPopulateAccessibilityEvent);
121  }
122
123  private static CharSequence getLabelForPhoneNumber(PrimaryInfo primaryInfo) {
124    if (primaryInfo.location != null) {
125      return primaryInfo.location;
126    }
127    if (!primaryInfo.nameIsNumber && !TextUtils.isEmpty(primaryInfo.number)) {
128      CharSequence spannedNumber = spanDisplayNumber(primaryInfo.number);
129      if (primaryInfo.label == null) {
130        return spannedNumber;
131      } else {
132        return TextUtils.concat(primaryInfo.label, " ", spannedNumber);
133      }
134    }
135    return null;
136  }
137
138  private static CharSequence spanDisplayNumber(String displayNumber) {
139    return PhoneNumberUtils.createTtsSpannable(
140        BidiFormatter.getInstance().unicodeWrap(displayNumber, TextDirectionHeuristics.LTR));
141  }
142
143  private static boolean isIncoming(PrimaryCallState state) {
144    return state.state == State.INCOMING || state.state == State.CALL_WAITING;
145  }
146}
147