自定义闹钟卡片AlarmClockCard

52 阅读3分钟
package com.alibaba.genie.panel.alarm.widget;

import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.icu.util.Calendar;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.AbsoluteSizeSpan;
import android.util.AttributeSet;
import android.widget.TextView;

import com.alibaba.ailabs.genie.utils.TimeUtils;
import com.alibaba.ailabs.tg.utils.CollectionUtils;
import com.alibaba.genie.panel.alarm.BR;
import com.alibaba.genie.panel.alarm.R;
import com.alibaba.genie.panel.alarm.databinding.IotWidgetAlarmClock1x1Binding;
import com.alibaba.genie.panel.alarm.repository.AlarmDatabaseHelper;
import com.alibaba.genie.panel.alarm.repository.AlarmSPManager;
import com.alibaba.genie.panel.alarm.repository.bean.alarm.AlarmInfo;
import com.alibaba.genie.panel.alarm.service.BaseService;
import com.alibaba.genie.panel.alarm.viewmodel.BaseAlarmViewModel;
import com.alibaba.genie.panel.basic.AbsApplication;
import com.alibaba.genie.panel.basic.base.BaseLayout;
import com.alibaba.genie.panel.basic.component.event.AlarmClockEvent;
import com.alibaba.genie.panel.basic.utils.SettingLog;
import com.alibaba.genie.panel.basic.utils.ViewUtils;
import com.aliyun.aligeniessp_1_0.models.ListAlarmsResponseBody;
import com.aliyun.alink.linksdk.tools.ALog;

import java.util.Date;
import java.util.List;


public class AlarmClockCard extends BaseLayout<IotWidgetAlarmClock1x1Binding, BaseAlarmViewModel> {

    private static final String TAG = AlarmClockCard.class.getSimpleName();
    private AlarmSPManager pref;

    AlarmDatabaseHelper alarmDatabaseHelper;

    private Handler mHandler;
    private static final int INTERVAL_REFRESH_TRIGGER_SPAN = 60_000;

    /**
     * 当前正在显示的闹钟数量 最多2个
     */
    private volatile int showAlarmCount = 0;

    /**
     * 演示模式下仅显示无闹钟状态
     */
    private boolean demoMode;


    public AlarmClockCard(@NonNull Context context) {
        this(context, null);
    }

    public AlarmClockCard(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AlarmClockCard(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    protected void initTypeArray(Context context, AttributeSet attrs, int defStyleAttr) {
        mHandler = new Handler(Looper.getMainLooper());
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.AlarmClockCard, defStyleAttr, 0);
        demoMode = typedArray.getBoolean(R.styleable.AlarmClockCard_n_demoMode, false);
        SettingLog.w(TAG, "initTypeArray demoMode:" + demoMode);
        typedArray.recycle();
    }

    @Override
    public int initContentView() {
        return R.layout.iot_widget_alarm_clock_1x1;
    }

    @Override
    public int initVariableId() {
        return BR._all;
    }

    @Override
    public boolean initData() {
        pref = AlarmSPManager.getInstance();
        return super.initData();
    }

    @Override
    public void initView() {
        if (demoMode) {
            showNoAlarm();
            return;
        }
        binding.btnAddAlarmClock.setOnClickListener(v -> {
//            showTwoAlarm();
//            AlarmClockActivity.open(getContext());
            Uri uri = Uri.parse("genie://com.alibaba.genie.panel.alarm/add");
            Intent intent = new Intent(Intent.ACTION_VIEW, uri);
            intent.setPackage(AbsApplication.getAppContext().getPackageName());
            getContext().startActivity(intent);
        });
        this.setOnClickListener(v -> {
            Uri uri = Uri.parse("genie://com.alibaba.genie.panel.alarm/list");
            Intent intent = new Intent(Intent.ACTION_VIEW, uri);
            intent.setPackage(AbsApplication.getAppContext().getPackageName());
            getContext().startActivity(intent);
        });
        showNoAlarm();
//        refreshAlarmList();
    }

    @Override
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);
        ViewUtils.setViewEnable(enabled, this);
        ViewUtils.setViewEnableAlpha(enabled, binding.btnAddAlarmClock);
    }

    @Override
    public void initViewObservable() {
        super.initViewObservable();
        if (demoMode) {
            return;
        }
        // 监听闹钟列表刷新
        liveBus.with(AlarmClockEvent.EVENT_UPDATED, AlarmClockEvent.class)
                .observe(getActivity(), alarmClockEvent -> refreshAlarmList());
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        ALog.w(TAG, "onAttachedToWindow");
        SettingLog.w(TAG, "onAttachedToWindow demoMode:" + demoMode);

        if (demoMode) {
            return;
        }
        refreshAlarmList();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        ALog.w(TAG, "onDetachedFromWindow");
        if (demoMode) {
            return;
        }
        stopCheckTriggerTimeSpan();
    }

    public void requestAlarmList() {
        liveBus.post(AlarmClockEvent.EVENT_REQUEST);
    }

    public void refreshAlarmList() {
        ALog.w(TAG, "refreshAlarmList");
        stopCheckTriggerTimeSpan();
        BaseService.postBgTask(() -> {
            List<ListAlarmsResponseBody.ListAlarmsResponseBodyResultModel> alarmList = pref.getAlarmList();
//        alarmList = alarmList == null ? null : alarmList.subList(0, Math.min(alarmList.size(), 2));
            if (CollectionUtils.isEmpty(alarmList)) {
                showAlarmCount = 0;
                showNoAlarm();
            } else {
                int size = alarmList.size();
                if (size == 1) {
                    showAlarmCount = 1;
                    showOneAlarm(alarmList.get(0));
                } else {
                    showAlarmCount = 2;
                    showTwoAlarm(alarmList.get(0), alarmList.get(1));
                }
            }
        });

    }

    /**
     * 显示没有闹钟UI
     */
    public void showNoAlarm() {
        showOneItemAndBtn(String.format("你可以说 “%s”", getGenieName()), "设置明天早上7点的闹钟");
        stopCheckTriggerTimeSpan();
    }

    protected String getGenieName() {
        return "天猫精灵";
    }


    /**
     * 显示一个闹钟UI
     */
    public void showOneAlarm(ListAlarmsResponseBody.ListAlarmsResponseBodyResultModel alarm) {
        showOneItemAndBtn(alarm.triggerTimeDesc, generateShortDate(alarm), null);
        setTriggerTimestamp(alarm, binding.tvAlarmTimeFirstSubInfo);
        startCheckTriggerTimeSpan();
    }

    /**
     * 显示两个闹钟UI
     */
    public void showTwoAlarm(ListAlarmsResponseBody.ListAlarmsResponseBodyResultModel alarmOne,
                             ListAlarmsResponseBody.ListAlarmsResponseBodyResultModel alarmTwo) {
        if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
            mHandler.post(() -> {
                ViewUtils.setViewsGone(binding.btnAddAlarmClock);
                ViewUtils.setViewsVisiable(
                        binding.llAlarmFirst,
                        binding.llAlarmSecond,
                        binding.tvAlarmFirstTime,
                        binding.tvAlarmTimeSecondSubInfo);
            });
        }

        String alarmOneTime = alarmOne.triggerTimeDesc;
        String alarmOneWeek = generateShortDate(alarmOne);

        setTriggerTimestamp(alarmOne, binding.tvAlarmTimeFirstSubInfo);

        String alarmTwoTime = alarmTwo.triggerTimeDesc;
        String alarmTwoWeek = generateShortDate(alarmTwo);
        if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
            mHandler.post(() -> {
                binding.tvAlarmFirstTime.setText(alarmOneTime);
                binding.tvAlarmFirstWeek.setText(alarmOneWeek);
                binding.tvAlarmSecondTime.setText(alarmTwoTime);
                binding.tvAlarmSecondWeek.setText(alarmTwoWeek);
            });
        }

        setTriggerTimestamp(alarmTwo, binding.tvAlarmTimeSecondSubInfo);

        startCheckTriggerTimeSpan();
    }

    private void setTriggerTimestamp(ListAlarmsResponseBody.ListAlarmsResponseBodyResultModel alarm,
                                     TextView tv) {
        long nextTriggerTimeStamp = getAlarmTriggerTimeStamp(alarm);
        tv.setTag(nextTriggerTimeStamp);
        refreshTriggerTimeSpan(tv, nextTriggerTimeStamp);
    }

    private void refreshTriggerTimeSpan(TextView tv) {
        refreshTriggerTimeSpan(tv, Long.MIN_VALUE);
    }

    private void refreshTriggerTimeSpan(TextView tv, long nextTriggerTimeStamp) {
        if (nextTriggerTimeStamp == Long.MIN_VALUE) {
            nextTriggerTimeStamp = (long) tv.getTag();
        }
        String alarmOneSubInfo = convertTriggerTimeSpan(nextTriggerTimeStamp);
        if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
            mHandler.post(() -> tv.setText(alarmOneSubInfo));
            return;
        }
        tv.setText(alarmOneSubInfo);
    }

    private String generateShortDate(ListAlarmsResponseBody.ListAlarmsResponseBodyResultModel alarm) {
        String triggerDateDesc = alarm.triggerDateDesc;
        if (!TextUtils.isEmpty(triggerDateDesc)) {//单次闹钟,"2024-7-23"
            String[] split = triggerDateDesc.split("-");
            if (split.length == 3) {
                return String.format("%s月%s日", split[1], split[2]);
            } else {
                return triggerDateDesc;
            }
        }
        String scheduleTypeDesc = alarm.scheduleTypeDesc;
        if (!TextUtils.isEmpty(scheduleTypeDesc) && scheduleTypeDesc.startsWith("每周")) {
            return scheduleTypeDesc.replace("/", "").replace("每周", "周/");
//            if (scheduleTypeDesc.length() < 6) {
//                return scheduleTypeDesc;
//            } else {
//                return scheduleTypeDesc.substring(0, 6) + "...";
//            }
        }
        return scheduleTypeDesc;
    }

    private AlarmDatabaseHelper getAlarmDBHelper() {
        if (alarmDatabaseHelper == null) {
            synchronized (this) {
                if (alarmDatabaseHelper == null) {
                    alarmDatabaseHelper = new AlarmDatabaseHelper(context.getApplicationContext());
                }
            }
        }
        return alarmDatabaseHelper;
    }

    private long getAlarmTriggerTimeStamp(ListAlarmsResponseBody.ListAlarmsResponseBodyResultModel alarm) {
        String triggerDateDesc = alarm.triggerDateDesc;
        String triggerTimeDesc = alarm.triggerTimeDesc;
        if (!TextUtils.isEmpty(triggerDateDesc) && !TextUtils.isEmpty(triggerTimeDesc)) {
            Date date = TimeUtils.getDateByString(triggerDateDesc + " " + triggerTimeDesc + ":00");
            if (date != null) {
                return date.getTime();
            }
            return 0;
        } else {
            AlarmInfo alarmInfo = getAlarmDBHelper().getAlarmByAlarmIdUpdateTime(String.valueOf(alarm.alarmId));
            if (alarmInfo == null || alarmInfo.getTime() == null) {
                return 0;
            }
            return alarmInfo.getTime().getTime();
        }
    }

    /**
     * 开始定时刷新剩余时间
     */
    private void startCheckTriggerTimeSpan() {
        ALog.w(TAG, "startCheckTriggerTimeSpan");
        if (mHandler != null) {
            mHandler.removeCallbacks(checkTriggerTimeSpanRunnable);
            mHandler.postDelayed(checkTriggerTimeSpanRunnable, getRemainSecondIn60() * 1000L);
        }
    }

    private int getRemainSecondIn60() {
        int second = Calendar.getInstance().get(Calendar.SECOND);
        return 60 - second;
    }

    public void stopCheckTriggerTimeSpan() {
        ALog.w(TAG, "stopCheckTriggerTimeSpan");
        if (mHandler != null) {
            mHandler.removeCallbacks(checkTriggerTimeSpanRunnable);
        }
    }

    private final Runnable checkTriggerTimeSpanRunnable = new Runnable() {
        @Override
        public void run() {
            ALog.w(TAG, "checkTriggerTimeSpanRunnable run");
            switch (showAlarmCount) {
                case 1:
                    refreshTriggerTimeSpan(binding.tvAlarmTimeFirstSubInfo);
                    break;
                case 2:
                    refreshTriggerTimeSpan(binding.tvAlarmTimeFirstSubInfo);
                    refreshTriggerTimeSpan(binding.tvAlarmTimeSecondSubInfo);
                    break;
                default:
                    break;
            }
            if (mHandler != null) {
                mHandler.postDelayed(this, INTERVAL_REFRESH_TRIGGER_SPAN);
            }
        }
    };

    private String convertTriggerTimeSpan(long triggerTimeStamp) {
        if (triggerTimeStamp <= 0) {
            return "";
        }
        long timeSpan = Math.abs(triggerTimeStamp - System.currentTimeMillis()) / 1_000;
        StringBuilder spanStr = new StringBuilder();
        int days = (int) (timeSpan / 86400);
        int hours = (int) (timeSpan % 86400 / 3600);
        int minutesSecond = (int) (timeSpan % 86400 % 3600);
        int minutes = minutesSecond % 60 > 0 ? minutesSecond / 60 + 1 : minutesSecond / 60;
        if (days > 0) {
            spanStr.append(days).append("天");
        }
        if (hours > 0) {
            spanStr.append(hours).append("小时");
        }
        if (minutes > 0) {
            spanStr.append(minutes).append("分钟");
        }
        if (spanStr.length() > 0) {
            spanStr.append("后响铃");
        } else {
            spanStr.append("即将响铃");
        }
        return spanStr.toString();
    }

    private void showOneItemAndBtn(String titleTwo, CharSequence subInfo) {
        showOneItemAndBtn(null, titleTwo, subInfo);
    }

    private void showOneItemAndBtn(String time, String date, CharSequence subInfo) {
        if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
            mHandler.post(() -> showOneItemAndBtn(time, date, subInfo));
            return;
        }
        binding.tvAlarmFirstTime.setText(time);
        binding.tvAlarmFirstWeek.setText(date);
        binding.tvAlarmTimeFirstSubInfo.setText(subInfo);

        ViewUtils.setViewsGone(binding.llAlarmSecond, binding.tvAlarmTimeSecondSubInfo, TextUtils.isEmpty(time) ? binding.tvAlarmFirstTime : null);
        ViewUtils.setViewsVisiable(binding.btnAddAlarmClock, !TextUtils.isEmpty(time) ? binding.tvAlarmFirstTime : null);
    }

    public static CharSequence sizeSpanString(String val, int fractionSize) {
        ALog.w("StringUtils", val);
        if (!TextUtils.isEmpty(val) && val.contains("--")) {
            SpannableStringBuilder stringBuilder = new SpannableStringBuilder(val);
            int gapIndex = val.indexOf(" ");
            stringBuilder.setSpan(new AbsoluteSizeSpan(fractionSize, true), 0, gapIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            return stringBuilder;
        }
        return val;
    }
}