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