blob: 0642baa884a66958f3ec9db7dba797b944d07d89 [file] [log] [blame]
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car.calendar;
import android.Manifest;
import android.annotation.ColorInt;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
import android.text.style.StyleSpan;
import android.view.LayoutInflater;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.DrawableRes;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.android.car.calendar.common.CalendarFormatter;
import com.android.car.calendar.common.Dialer;
import com.android.car.calendar.common.Event;
import com.android.car.calendar.common.Navigator;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import javax.annotation.Nullable;
/** An item in the calendar list view that shows a single event. */
class EventCalendarItem implements CalendarItem {
private final Event mEvent;
private final CalendarFormatter mFormatter;
private final Navigator mNavigator;
private final Dialer mDialer;
private final CarCalendarActivity mCarCalendarActivity;
EventCalendarItem(
Event event,
CalendarFormatter formatter,
Navigator navigator,
Dialer dialer,
CarCalendarActivity carCalendarActivity) {
mEvent = event;
mFormatter = formatter;
mNavigator = navigator;
mDialer = dialer;
mCarCalendarActivity = carCalendarActivity;
}
@Override
public Type getType() {
return Type.EVENT;
}
@Override
public void bind(RecyclerView.ViewHolder holder) {
EventViewHolder eventViewHolder = (EventViewHolder) holder;
EventAction primaryAction;
if (!Strings.isNullOrEmpty(mEvent.getLocation())) {
primaryAction =
new EventAction(
R.drawable.ic_navigation_gm2_24px,
mEvent.getLocation(),
(view) -> mNavigator.navigate(mEvent.getLocation()));
} else {
primaryAction =
new EventAction(
R.drawable.ic_navigation_gm2_24px, /* descriptionText */
null, /* onClickHandler */
null);
}
EventAction secondaryAction;
Dialer.NumberAndAccess numberAndAccess = mEvent.getNumberAndAccess();
if (numberAndAccess != null) {
String dialDescriptionText;
if (numberAndAccess.getAccess() != null) {
dialDescriptionText =
mCarCalendarActivity.getString(
R.string.phone_number_with_pin,
numberAndAccess.getNumber(),
numberAndAccess.getAccess());
} else {
dialDescriptionText =
mCarCalendarActivity.getString(
R.string.phone_number, numberAndAccess.getNumber());
}
secondaryAction =
new EventAction(
R.drawable.ic_phone_gm2_24px,
dialDescriptionText,
(view) -> dial(numberAndAccess));
} else {
secondaryAction =
new EventAction(
R.drawable.ic_phone_gm2_24px,
/* descriptionText */ null,
/* onClickListener= */ null);
}
String timeRangeText;
if (!mEvent.isAllDay()) {
timeRangeText =
mFormatter.getTimeRangeText(mEvent.getStartInstant(), mEvent.getEndInstant());
} else {
timeRangeText = mCarCalendarActivity.getString(R.string.all_day_event);
}
eventViewHolder.update(
timeRangeText,
mEvent.getTitle(),
mEvent.getCalendarDetails().getColor(),
mEvent.getCalendarDetails().getName(),
primaryAction,
secondaryAction,
mEvent.getStatus());
}
private void dial(Dialer.NumberAndAccess numberAndAccess) {
mCarCalendarActivity.runWithPermission(
Manifest.permission.CALL_PHONE,
() -> {
if (!mDialer.dial(numberAndAccess)) {
Toast.makeText(mCarCalendarActivity, R.string.no_dialler, Toast.LENGTH_LONG)
.show();
}
});
}
private static class EventAction {
@DrawableRes private final int mIconResourceId;
@Nullable private final String mDescriptionText;
@Nullable private final OnClickListener mOnClickListener;
private EventAction(
int iconResourceId,
@Nullable String descriptionText,
@Nullable OnClickListener onClickListener) {
this.mIconResourceId = iconResourceId;
this.mDescriptionText = descriptionText;
this.mOnClickListener = onClickListener;
}
}
static class EventViewHolder extends RecyclerView.ViewHolder {
private static final String FIELD_SEPARATOR = ", ";
private static final Joiner JOINER = Joiner.on(FIELD_SEPARATOR).skipNulls();
private final TextView mTitleView;
private final TextView mDescriptionView;
private final DrawableStateImageButton mPrimaryActionButton;
private final DrawableStateImageButton mSecondaryActionButton;
private final int mCalendarIndicatorSize;
private final int mCalendarIndicatorPadding;
@ColorInt private final int mTimeTextColor;
EventViewHolder(ViewGroup parent) {
super(
LayoutInflater.from(parent.getContext())
.inflate(R.layout.event_item, parent, /* attachToRoot= */ false));
mTitleView = itemView.findViewById(R.id.event_title);
mDescriptionView = itemView.findViewById(R.id.description_text);
mPrimaryActionButton = itemView.findViewById(R.id.primary_action_button);
mSecondaryActionButton = itemView.findViewById(R.id.secondary_action_button);
mCalendarIndicatorSize =
(int) parent.getResources().getDimension(R.dimen.car_calendar_indicator_width);
mCalendarIndicatorPadding =
(int) parent.getResources().getDimension(R.dimen.car_ui_padding_1);
mTimeTextColor =
ContextCompat.getColor(parent.getContext(), R.color.car_ui_text_color_primary);
}
void update(
String timeRangeText,
String title,
@ColorInt int calendarColor,
String calendarName,
EventAction primaryAction,
EventAction secondaryAction,
Event.Status status) {
mTitleView.setText(title);
String detailText = null;
if (primaryAction.mDescriptionText != null) {
detailText = primaryAction.mDescriptionText;
} else if (secondaryAction.mDescriptionText != null) {
detailText = secondaryAction.mDescriptionText;
}
SpannableString descriptionSpannable =
createDescriptionSpannable(
calendarColor, calendarName, timeRangeText, detailText);
mDescriptionView.setText(descriptionSpannable);
// Strike-through all text fields when the event was declined.
setTextFlags(
Paint.STRIKE_THRU_TEXT_FLAG,
/* add= */ status.equals(Event.Status.DECLINED),
mTitleView,
mDescriptionView);
mPrimaryActionButton.setImageResource(primaryAction.mIconResourceId);
if (primaryAction.mOnClickListener != null) {
mPrimaryActionButton.setEnabled(true);
mPrimaryActionButton.setContentDescription(primaryAction.mDescriptionText);
mPrimaryActionButton.setOnClickListener(primaryAction.mOnClickListener);
} else {
mPrimaryActionButton.setEnabled(false);
mPrimaryActionButton.setContentDescription(null);
mPrimaryActionButton.setOnClickListener(null);
}
mSecondaryActionButton.setImageResource(secondaryAction.mIconResourceId);
if (secondaryAction.mOnClickListener != null) {
mSecondaryActionButton.setEnabled(true);
mSecondaryActionButton.setContentDescription(secondaryAction.mDescriptionText);
mSecondaryActionButton.setOnClickListener(secondaryAction.mOnClickListener);
} else {
mSecondaryActionButton.setEnabled(false);
mSecondaryActionButton.setContentDescription(null);
mSecondaryActionButton.setOnClickListener(null);
}
}
private SpannableString createDescriptionSpannable(
@ColorInt int calendarColor,
String calendarName,
String timeRangeText,
@Nullable String detailText) {
ShapeDrawable calendarIndicatorDrawable = new ShapeDrawable(new OvalShape());
calendarIndicatorDrawable.getPaint().setColor(calendarColor);
calendarIndicatorDrawable.setBounds(
/* left= */ 0,
/* top= */ 0,
/* right= */ mCalendarIndicatorSize,
/* bottom= */ mCalendarIndicatorSize);
// Add padding to the right of the image to separate it from the text.
InsetDrawable insetDrawable =
new InsetDrawable(
calendarIndicatorDrawable,
/* insetLeft= */ 0,
/* insetTop= */ 0,
/* insetRight= */ mCalendarIndicatorPadding,
/* insetBottom= */ 0);
insetDrawable.setBounds(
/* left= */ 0,
/* top= */ 0,
/* right= */ mCalendarIndicatorSize + mCalendarIndicatorPadding,
/* bottom= */ mCalendarIndicatorSize);
String descriptionText =
JOINER.join(Lists.newArrayList(calendarName, timeRangeText, detailText));
SpannableString descriptionSpannable = new SpannableString(descriptionText);
ImageSpan calendarIndicatorSpan =
new ImageSpan(insetDrawable, ImageSpan.ALIGN_BASELINE);
int calendarNameEnd = calendarName.length() + FIELD_SEPARATOR.length();
descriptionSpannable.setSpan(
calendarIndicatorSpan, /* start= */ 0, calendarNameEnd, /* flags= */ 0);
int timeEnd = calendarNameEnd + timeRangeText.length();
descriptionSpannable.setSpan(
new StyleSpan(Typeface.BOLD), calendarNameEnd, timeEnd, /* flags= */ 0);
descriptionSpannable.setSpan(
new ForegroundColorSpan(mTimeTextColor),
calendarNameEnd,
timeEnd,
/* flags= */ 0);
return descriptionSpannable;
}
/**
* Set paint flags on the given text views.
*
* @param flags The combined {@link Paint} flags to set or unset.
* @param set Set the flags if true, otherwise unset.
* @param views The views to apply the flags to.
*/
private void setTextFlags(int flags, boolean set, TextView... views) {
for (TextView view : views) {
if (set) {
view.setPaintFlags(view.getPaintFlags() | flags);
} else {
view.setPaintFlags(view.getPaintFlags() & ~flags);
}
}
}
}
}