1 /* 2 * Copyright (C) 2015 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.deskclock.alarms.dataadapter; 18 19 import android.content.Context; 20 import android.database.Cursor; 21 import android.os.Bundle; 22 import android.os.Vibrator; 23 import android.support.v7.widget.RecyclerView; 24 import android.view.LayoutInflater; 25 import android.view.View; 26 import android.view.ViewGroup; 27 28 import com.android.deskclock.LogUtils; 29 import com.android.deskclock.R; 30 import com.android.deskclock.alarms.AlarmTimeClickHandler; 31 import com.android.deskclock.alarms.ScrollHandler; 32 import com.android.deskclock.provider.Alarm; 33 import com.android.deskclock.provider.AlarmInstance; 34 35 /** 36 * Data adapter for alarm time items. 37 */ 38 public final class AlarmTimeAdapter extends RecyclerView.Adapter<AlarmTimeViewHolder> { 39 private static final String TAG = "CwAlarm"; 40 private static final String KEY_EXPANDED_ID = "expandedId"; 41 private static final int VIEW_TYPE_ALARM_TIME_COLLAPSED = R.layout.alarm_time_collapsed; 42 private static final int VIEW_TYPE_ALARM_TIME_EXPANDED = R.layout.alarm_time_expanded; 43 44 private final Context mContext; 45 private final LayoutInflater mInflater; 46 47 private final AlarmTimeClickHandler mAlarmTimeClickHandler; 48 private final ScrollHandler mScrollHandler; 49 50 private final boolean mHasVibrator; 51 private int mExpandedPosition = -1; 52 private long mExpandedId = Alarm.INVALID_ID; 53 private Cursor mCursor; 54 AlarmTimeAdapter(Context context, Bundle savedState, AlarmTimeClickHandler alarmTimeClickHandler, ScrollHandler smoothScrollController)55 public AlarmTimeAdapter(Context context, Bundle savedState, 56 AlarmTimeClickHandler alarmTimeClickHandler, ScrollHandler smoothScrollController) { 57 mContext = context; 58 mInflater = LayoutInflater.from(context); 59 mScrollHandler = smoothScrollController; 60 mAlarmTimeClickHandler = alarmTimeClickHandler; 61 mHasVibrator = ((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE)) 62 .hasVibrator(); 63 if (savedState != null) { 64 mExpandedId = savedState.getLong(KEY_EXPANDED_ID, Alarm.INVALID_ID); 65 } 66 67 setHasStableIds(true); 68 } 69 70 @Override onCreateViewHolder(ViewGroup parent, int viewType)71 public AlarmTimeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 72 final View v = mInflater.inflate(viewType, parent, false /* attachToRoot */); 73 if (viewType == VIEW_TYPE_ALARM_TIME_COLLAPSED) { 74 return new CollapsedAlarmViewHolder(v, mAlarmTimeClickHandler, this); 75 } else { 76 return new ExpandedAlarmViewHolder(v, mHasVibrator, mAlarmTimeClickHandler, this); 77 } 78 } 79 80 @Override onViewRecycled(AlarmTimeViewHolder viewHolder)81 public void onViewRecycled(AlarmTimeViewHolder viewHolder) { 82 super.onViewRecycled(viewHolder); 83 viewHolder.clearData(); 84 } 85 86 @Override onBindViewHolder(AlarmTimeViewHolder viewHolder, int position)87 public void onBindViewHolder(AlarmTimeViewHolder viewHolder, int position) { 88 if (!mCursor.moveToPosition(position)) { 89 LogUtils.e(TAG, "Failed to bind alarm " + position); 90 return; 91 } 92 final Alarm alarm = new Alarm(mCursor); 93 final AlarmInstance alarmInstance = alarm.canPreemptivelyDismiss() 94 ? new AlarmInstance(mCursor, true /* joinedTable */) : null; 95 viewHolder.bindAlarm(mContext, alarm, alarmInstance); 96 } 97 98 @Override getItemCount()99 public int getItemCount() { 100 return mCursor == null ? 0 : mCursor.getCount(); 101 } 102 103 @Override getItemId(int position)104 public long getItemId(int position) { 105 if (mCursor == null || !mCursor.moveToPosition(position)) { 106 return RecyclerView.NO_ID; 107 } 108 // TODO: Directly read id instead of instantiating Alarm object. 109 return new Alarm(mCursor).id; 110 } 111 112 @Override getItemViewType(int position)113 public int getItemViewType(int position) { 114 final long stableId = getItemId(position); 115 return stableId != RecyclerView.NO_ID && stableId == mExpandedId 116 ? VIEW_TYPE_ALARM_TIME_EXPANDED : VIEW_TYPE_ALARM_TIME_COLLAPSED; 117 } 118 saveInstance(Bundle outState)119 public void saveInstance(Bundle outState) { 120 outState.putLong(KEY_EXPANDED_ID, mExpandedId); 121 } 122 123 /** 124 * Request the UI to expand the alarm at selected position and scroll it into view. 125 */ expand(int position)126 public void expand(int position) { 127 final long stableId = getItemId(position); 128 if (mExpandedId == stableId) { 129 return; 130 } 131 mExpandedId = stableId; 132 mScrollHandler.smoothScrollTo(position); 133 if (mExpandedPosition >= 0) { 134 notifyItemChanged(mExpandedPosition); 135 } 136 mExpandedPosition = position; 137 notifyItemChanged(position); 138 } 139 collapse(int position)140 public void collapse(int position) { 141 mExpandedId = Alarm.INVALID_ID; 142 mExpandedPosition = -1; 143 notifyItemChanged(position); 144 } 145 146 /** 147 * Swaps the adapter to a new data source. 148 * 149 * @param cursor A cursor generated by Cursor loader from {@link Alarm#getAlarmsCursorLoader}. 150 */ swapCursor(Cursor cursor)151 public void swapCursor(Cursor cursor) { 152 if (mCursor == cursor) { 153 return; 154 } 155 if (mCursor != null) { 156 mCursor.close(); 157 } 158 mCursor = cursor; 159 notifyDataSetChanged(); 160 } 161 } 162