Java tutorial
/* * * Copyright (c) Microsoft. All rights reserved. * Licensed under the MIT license. * * Project Oxford: http://ProjectOxford.ai * * Project Oxford Mimicker Alarm Github: * https://github.com/Microsoft/ProjectOxford-Apps-MimickerAlarm * * Copyright (c) Microsoft Corporation * All rights reserved. * * MIT License: * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ package com.microsoft.mimickeralarm.ringing; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.content.ClipData; import android.content.Context; import android.graphics.drawable.AnimationDrawable; import android.os.Build; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.util.Log; import android.view.DragEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.ImageView; import android.widget.TextView; import com.microsoft.mimickeralarm.R; import com.microsoft.mimickeralarm.model.Alarm; import com.microsoft.mimickeralarm.model.AlarmList; import com.microsoft.mimickeralarm.utilities.DateTimeUtilities; import com.microsoft.mimickeralarm.utilities.Loggable; import com.microsoft.mimickeralarm.utilities.Logger; import com.microsoft.mimickeralarm.utilities.GeneralUtilities; import java.util.UUID; /** * This class encapsulates the user experience of the alarm ringing screen. * * The user can drag and drop the animating clock to the snooze or dismiss buttons, which will * call back into the hosting activity (AlarmRingingActivity) to take care of the screen * transitioning to the appropriate next screen. */ public class AlarmRingingFragment extends Fragment { public static final String RINGING_FRAGMENT_TAG = "ringing_fragment"; private static final String ARGS_ALARM_ID = "alarm_id"; private static final int CLOCK_ANIMATION_DURATION = 1500; private static final int SHOW_CLOCK_AFTER_UNSUCCESSFUL_DRAG_DELAY = 250; public final String TAG = this.getClass().getSimpleName(); RingingResultListener mCallback; private ImageView mAlarmRingingClock; private ImageView mLeftArrowImage; private ImageView mRightArrowImage; private ObjectAnimator mClockAnimation; private AnimationDrawable mLeftArrowAnimation; private AnimationDrawable mRightArrowAnimation; // A zone value to tell us If the current dragged clock is on the left of the clock, right of the clock, or near the middle of view. private DragZone mDragZone; // The threshold of gap to decide if the drag of the clock should cause // us to hide left arrow or right arrow private float mDragThreshold; private boolean mShowClockOnDragEnd; private Alarm mAlarm; public static AlarmRingingFragment newInstance(String alarmId) { AlarmRingingFragment fragment = new AlarmRingingFragment(); Bundle bundle = new Bundle(1); bundle.putString(ARGS_ALARM_ID, alarmId); fragment.setArguments(bundle); return fragment; } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Logger.init(getActivity()); Bundle args = getArguments(); UUID alarmId = UUID.fromString(args.getString(ARGS_ALARM_ID)); mAlarm = AlarmList.get(getContext()).getAlarm(alarmId); View view = inflater.inflate(R.layout.fragment_alarm_ringing, container, false); if (android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) { TextView timeField = (TextView) view.findViewById(R.id.alarm_ringing_time); timeField.setText(DateTimeUtilities.getUserTimeString(getContext(), mAlarm.getTimeHour(), mAlarm.getTimeMinute())); } TextView dateField = (TextView) view.findViewById(R.id.alarm_ringing_date); dateField.setText(DateTimeUtilities.getFullDateStringForNow()); String name = mAlarm.getTitle(); TextView titleField = (TextView) view.findViewById(R.id.alarm_ringing_title); titleField.setText(name); ImageView dismissButton = (ImageView) view.findViewById(R.id.alarm_ringing_dismiss); dismissButton.setOnDragListener(new View.OnDragListener() { @Override public boolean onDrag(View v, DragEvent event) { switch (event.getAction()) { case DragEvent.ACTION_DROP: dismissAlarm(); break; case DragEvent.ACTION_DRAG_ENDED: if (mShowClockOnDragEnd) { mAlarmRingingClock.postDelayed(new Runnable() { @Override public void run() { mAlarmRingingClock.setVisibility(View.VISIBLE); } }, SHOW_CLOCK_AFTER_UNSUCCESSFUL_DRAG_DELAY); } break; default: break; } return true; } }); // Dismiss ringing if someone presses the dismiss button directly dismissButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { dismissAlarm(); } }); ImageView snoozeButton = (ImageView) view.findViewById(R.id.alarm_ringing_snooze); snoozeButton.setOnDragListener(new View.OnDragListener() { @Override public boolean onDrag(View v, DragEvent event) { switch (event.getAction()) { case DragEvent.ACTION_DROP: mCallback.onRingingSnooze(); break; default: break; } return true; } }); // Snooze ringing if someone presses the snooze button directly snoozeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mCallback.onRingingSnooze(); } }); // Allow the view to listen to the drag event to update arrow animations accordingly view.setOnDragListener(new View.OnDragListener() { @Override public boolean onDrag(View v, DragEvent event) { switch (event.getAction()) { case DragEvent.ACTION_DRAG_LOCATION: // Update the left/right arrow visibility based on the current drag location. onClockDragLocation(event.getX(), event.getY(), v.getWidth() / 2); break; case DragEvent.ACTION_DROP: // The user has dropped the drag, but it is dropped within the view, instead of the target // drop zones to dismiss or snooze. // Restore to show both left arrow and right arrow animations. mDragZone = DragZone.NEAR_MIDDLE_OF_VIEW; updateArrowsBasedOnDragZone(mDragZone); break; default: break; } return true; } }); mAlarmRingingClock = (ImageView) view.findViewById(R.id.alarm_ringing_clock); mAlarmRingingClock.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { ClipData dragData = ClipData.newPlainText("", ""); View.DragShadowBuilder shadow = new View.DragShadowBuilder(mAlarmRingingClock); mAlarmRingingClock.startDrag(dragData, shadow, null, 0); mAlarmRingingClock.setVisibility(View.INVISIBLE); return true; } else { return false; } } }); initializeClockAnimation(view); Loggable.AppAction appAction = new Loggable.AppAction(Loggable.Key.APP_ALARM_RINGING); appAction.putJSON(mAlarm.toJSON()); Logger.track(appAction); return view; } private void onClockDragLocation(float x, float y, int viewMiddleX) { DragZone newDragZone; if (x < viewMiddleX - mDragThreshold) { newDragZone = DragZone.DRAGGING_TO_LEFT; } else if (x > viewMiddleX + mDragThreshold) { newDragZone = DragZone.DRAGGING_TO_RIGHT; } else { newDragZone = DragZone.NEAR_MIDDLE_OF_VIEW; } if (newDragZone != mDragZone) { mDragZone = newDragZone; updateArrowsBasedOnDragZone(mDragZone); } } private void updateArrowsBasedOnDragZone(DragZone dragZone) { switch (mDragZone) { case NEAR_MIDDLE_OF_VIEW: // Show both arrow animations mLeftArrowImage.setVisibility(View.VISIBLE); mRightArrowImage.setVisibility(View.VISIBLE); break; case DRAGGING_TO_LEFT: // Show only the left arrow animation to guide user to drag to the left mLeftArrowImage.setVisibility(View.VISIBLE); mRightArrowImage.setVisibility(View.INVISIBLE); break; case DRAGGING_TO_RIGHT: // Show only the right arrow animation to guide user to drag to the left mLeftArrowImage.setVisibility(View.INVISIBLE); mRightArrowImage.setVisibility(View.VISIBLE); break; } } private void dismissAlarm() { mShowClockOnDragEnd = false; Loggable.UserAction userAction = new Loggable.UserAction(Loggable.Key.ACTION_ALARM_DISMISS); userAction.putJSON(mAlarm.toJSON()); Logger.track(userAction); mCallback.onRingingDismiss(); } @Override public void onAttach(Context context) { super.onAttach(context); mCallback = (RingingResultListener) context; } @Override public void onDetach() { super.onDetach(); mCallback = null; } @Override public void onResume() { super.onResume(); Log.d(TAG, "Entered onResume!"); mShowClockOnDragEnd = true; mDragZone = DragZone.NEAR_MIDDLE_OF_VIEW; mDragThreshold = mAlarmRingingClock.getWidth() / 2; mAlarmRingingClock.setVisibility(View.VISIBLE); mClockAnimation.start(); mLeftArrowAnimation.start(); mRightArrowAnimation.start(); GeneralUtilities.registerCrashReport(getActivity()); } @Override public void onPause() { super.onPause(); Log.d(TAG, "Entered onPause!"); mClockAnimation.cancel(); mLeftArrowAnimation.stop(); mRightArrowAnimation.stop(); } @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "Entered onDestroy!"); } private void initializeClockAnimation(View view) { // Show a growing clock and then shrinking again repeatedly PropertyValuesHolder scaleXAnimation = PropertyValuesHolder.ofFloat(View.SCALE_X, 1f, 1.2f, 1f); PropertyValuesHolder scaleYAnimation = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1f, 1.2f, 1f); mClockAnimation = ObjectAnimator.ofPropertyValuesHolder(mAlarmRingingClock, scaleXAnimation, scaleYAnimation); mClockAnimation.setDuration(CLOCK_ANIMATION_DURATION); mClockAnimation.setInterpolator(new AccelerateDecelerateInterpolator()); mClockAnimation.setRepeatCount(ValueAnimator.INFINITE); mLeftArrowImage = (ImageView) view.findViewById(R.id.alarm_ringing_left_arrow); mLeftArrowImage.setBackgroundResource(R.drawable.ringing_left_arrow_animation); mRightArrowImage = (ImageView) view.findViewById(R.id.alarm_ringing_right_arrow); mRightArrowImage.setBackgroundResource(R.drawable.ringing_right_arrow_animation); mLeftArrowAnimation = (AnimationDrawable) mLeftArrowImage.getBackground(); mRightArrowAnimation = (AnimationDrawable) mRightArrowImage.getBackground(); } public interface RingingResultListener { void onRingingSnooze(); void onRingingDismiss(); } enum DragZone { NEAR_MIDDLE_OF_VIEW, DRAGGING_TO_LEFT, DRAGGING_TO_RIGHT } }