| /* |
| * Copyright (C) 2015 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.deskclock; |
| |
| import android.app.Activity; |
| import android.app.LoaderManager; |
| import android.content.Intent; |
| import android.content.Loader; |
| import android.database.Cursor; |
| import android.media.RingtoneManager; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.support.design.widget.Snackbar; |
| import android.support.v7.widget.LinearLayoutManager; |
| import android.support.v7.widget.RecyclerView; |
| import android.text.format.DateFormat; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| |
| import com.android.deskclock.alarms.AlarmTimeClickHandler; |
| import com.android.deskclock.alarms.AlarmUpdateHandler; |
| import com.android.deskclock.alarms.ScrollHandler; |
| import com.android.deskclock.alarms.TimePickerCompat; |
| import com.android.deskclock.alarms.dataadapter.AlarmTimeAdapter; |
| import com.android.deskclock.data.DataModel; |
| import com.android.deskclock.provider.Alarm; |
| import com.android.deskclock.widget.EmptyViewController; |
| import com.android.deskclock.widget.toast.SnackbarManager; |
| import com.android.deskclock.widget.toast.ToastManager; |
| |
| /** |
| * A fragment that displays a list of alarm time and allows interaction with them. |
| */ |
| public final class AlarmClockFragment extends DeskClockFragment implements |
| LoaderManager.LoaderCallbacks<Cursor>, ScrollHandler, TimePickerCompat.OnTimeSetListener { |
| |
| // This extra is used when receiving an intent to create an alarm, but no alarm details |
| // have been passed in, so the alarm page should start the process of creating a new alarm. |
| public static final String ALARM_CREATE_NEW_INTENT_EXTRA = "deskclock.create.new"; |
| |
| // This extra is used when receiving an intent to scroll to specific alarm. If alarm |
| // can not be found, and toast message will pop up that the alarm has be deleted. |
| public static final String SCROLL_TO_ALARM_INTENT_EXTRA = "deskclock.scroll.to.alarm"; |
| |
| // Views |
| private ViewGroup mMainLayout; |
| private RecyclerView mRecyclerView; |
| |
| // Data |
| private long mScrollToAlarmId = Alarm.INVALID_ID; |
| private Loader mCursorLoader = null; |
| |
| // Controllers |
| private AlarmTimeAdapter mAlarmTimeAdapter; |
| private AlarmUpdateHandler mAlarmUpdateHandler; |
| private EmptyViewController mEmptyViewController; |
| private AlarmTimeClickHandler mAlarmTimeClickHandler; |
| private LinearLayoutManager mLayoutManager; |
| |
| @Override |
| public void processTimeSet(int hourOfDay, int minute) { |
| mAlarmTimeClickHandler.processTimeSet(hourOfDay, minute); |
| } |
| |
| @Override |
| public void onCreate(Bundle savedState) { |
| super.onCreate(savedState); |
| mCursorLoader = getLoaderManager().initLoader(0, null, this); |
| } |
| |
| @Override |
| public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) { |
| // Inflate the layout for this fragment |
| final View v = inflater.inflate(R.layout.alarm_clock, container, false); |
| |
| mRecyclerView = (RecyclerView) v.findViewById(R.id.alarms_recycler_view); |
| mLayoutManager = new LinearLayoutManager(getActivity()); |
| mRecyclerView.setLayoutManager(mLayoutManager); |
| mMainLayout = (ViewGroup) v.findViewById(R.id.main); |
| mAlarmUpdateHandler = new AlarmUpdateHandler(getActivity(), this, mMainLayout); |
| mEmptyViewController = new EmptyViewController(mMainLayout, mRecyclerView, |
| v.findViewById(R.id.alarms_empty_view)); |
| mAlarmTimeClickHandler = new AlarmTimeClickHandler(this, savedState, mAlarmUpdateHandler, |
| this); |
| mAlarmTimeAdapter = new AlarmTimeAdapter(getActivity(), savedState, |
| mAlarmTimeClickHandler, this); |
| mRecyclerView.setAdapter(mAlarmTimeAdapter); |
| |
| return v; |
| } |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| |
| final DeskClock activity = (DeskClock) getActivity(); |
| if (activity.getSelectedTab() == DeskClock.ALARM_TAB_INDEX) { |
| setFabAppearance(); |
| setLeftRightButtonAppearance(); |
| } |
| |
| // Check if another app asked us to create a blank new alarm. |
| final Intent intent = getActivity().getIntent(); |
| if (intent.hasExtra(ALARM_CREATE_NEW_INTENT_EXTRA)) { |
| if (intent.getBooleanExtra(ALARM_CREATE_NEW_INTENT_EXTRA, false)) { |
| // An external app asked us to create a blank alarm. |
| startCreatingAlarm(); |
| } |
| |
| // Remove the CREATE_NEW extra now that we've processed it. |
| intent.removeExtra(ALARM_CREATE_NEW_INTENT_EXTRA); |
| } else if (intent.hasExtra(SCROLL_TO_ALARM_INTENT_EXTRA)) { |
| long alarmId = intent.getLongExtra(SCROLL_TO_ALARM_INTENT_EXTRA, Alarm.INVALID_ID); |
| if (alarmId != Alarm.INVALID_ID) { |
| setSmoothScrollStableId(alarmId); |
| if (mCursorLoader != null && mCursorLoader.isStarted()) { |
| // We need to force a reload here to make sure we have the latest view |
| // of the data to scroll to. |
| mCursorLoader.forceLoad(); |
| } |
| } |
| |
| // Remove the SCROLL_TO_ALARM extra now that we've processed it. |
| intent.removeExtra(SCROLL_TO_ALARM_INTENT_EXTRA); |
| } |
| } |
| |
| @Override |
| public void smoothScrollTo(int position) { |
| mLayoutManager.scrollToPositionWithOffset(position, 20); |
| } |
| |
| @Override |
| public void onSaveInstanceState(Bundle outState) { |
| super.onSaveInstanceState(outState); |
| mAlarmTimeAdapter.saveInstance(outState); |
| mAlarmTimeClickHandler.saveInstance(outState); |
| } |
| |
| @Override |
| public void onDestroy() { |
| super.onDestroy(); |
| ToastManager.cancelToast(); |
| } |
| |
| @Override |
| public void onPause() { |
| super.onPause(); |
| // When the user places the app in the background by pressing "home", |
| // dismiss the toast bar. However, since there is no way to determine if |
| // home was pressed, just dismiss any existing toast bar when restarting |
| // the app. |
| mAlarmUpdateHandler.hideUndoBar(); |
| } |
| |
| public void setLabel(Alarm alarm, String label) { |
| alarm.label = label; |
| mAlarmUpdateHandler.asyncUpdateAlarm(alarm, false, true); |
| } |
| |
| @Override |
| public Loader<Cursor> onCreateLoader(int id, Bundle args) { |
| return Alarm.getAlarmsCursorLoader(getActivity()); |
| } |
| |
| @Override |
| public void onLoadFinished(Loader<Cursor> cursorLoader, final Cursor data) { |
| mEmptyViewController.setEmpty(data.getCount() == 0); |
| mAlarmTimeAdapter.swapCursor(data); |
| if (mScrollToAlarmId != Alarm.INVALID_ID) { |
| scrollToAlarm(mScrollToAlarmId); |
| setSmoothScrollStableId(Alarm.INVALID_ID); |
| } |
| } |
| |
| /** |
| * Scroll to alarm with given alarm id. |
| * |
| * @param alarmId The alarm id to scroll to. |
| */ |
| private void scrollToAlarm(long alarmId) { |
| final int alarmCount = mAlarmTimeAdapter.getItemCount(); |
| int alarmPosition = -1; |
| for (int i = 0; i < alarmCount; i++) { |
| long id = mAlarmTimeAdapter.getItemId(i); |
| if (id == alarmId) { |
| alarmPosition = i; |
| break; |
| } |
| } |
| |
| if (alarmPosition >= 0) { |
| mAlarmTimeAdapter.expand(alarmPosition); |
| } else { |
| // Trying to display a deleted alarm should only happen from a missed notification for |
| // an alarm that has been marked deleted after use. |
| SnackbarManager.show(Snackbar.make(mMainLayout, R.string |
| .missed_alarm_has_been_deleted, Snackbar.LENGTH_LONG)); |
| } |
| } |
| |
| @Override |
| public void onLoaderReset(Loader<Cursor> cursorLoader) { |
| mAlarmTimeAdapter.swapCursor(null); |
| } |
| |
| @Override |
| public void onActivityResult(int requestCode, int resultCode, Intent data) { |
| if (resultCode != Activity.RESULT_OK) { |
| return; |
| } |
| |
| switch (requestCode) { |
| case R.id.request_code_ringtone: |
| // Extract the selected ringtone uri. |
| Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI); |
| if (uri == null) { |
| uri = Alarm.NO_RINGTONE_URI; |
| } |
| |
| // Update the default ringtone for future new alarms. |
| DataModel.getDataModel().setDefaultAlarmRingtoneUri(uri); |
| |
| // Set the ringtone uri on the alarm. |
| final Alarm alarm = mAlarmTimeClickHandler.getSelectedAlarm(); |
| if (alarm == null) { |
| LogUtils.e("Could not get selected alarm to set ringtone"); |
| return; |
| } |
| alarm.alert = uri; |
| |
| // Save the change to alarm. |
| mAlarmUpdateHandler.asyncUpdateAlarm(alarm, false /* popToast */, |
| true /* minorUpdate */); |
| break; |
| default: |
| LogUtils.w("Unhandled request code in onActivityResult: " + requestCode); |
| } |
| } |
| |
| @Override |
| public void setSmoothScrollStableId(long stableId) { |
| mScrollToAlarmId = stableId; |
| } |
| |
| @Override |
| public void onFabClick(View view) { |
| mAlarmUpdateHandler.hideUndoBar(); |
| startCreatingAlarm(); |
| } |
| |
| @Override |
| public void setFabAppearance() { |
| if (mFab == null || getDeskClock().getSelectedTab() != DeskClock.ALARM_TAB_INDEX) { |
| return; |
| } |
| mFab.setVisibility(View.VISIBLE); |
| mFab.setImageResource(R.drawable.ic_add_white_24dp); |
| mFab.setContentDescription(getString(R.string.button_alarms)); |
| } |
| |
| @Override |
| public void setLeftRightButtonAppearance() { |
| if (mLeftButton == null || mRightButton == null || |
| getDeskClock().getSelectedTab() != DeskClock.ALARM_TAB_INDEX) { |
| return; |
| } |
| mLeftButton.setVisibility(View.INVISIBLE); |
| mRightButton.setVisibility(View.INVISIBLE); |
| } |
| |
| private void startCreatingAlarm() { |
| mAlarmTimeClickHandler.clearSelectedAlarm(); |
| TimePickerCompat.showTimeEditDialog(this, null /* alarm */, |
| DateFormat.is24HourFormat(getActivity())); |
| } |
| } |