From 3fc32c45f5efc4ce4b91cbcdd925d9b30f67046e Mon Sep 17 00:00:00 2001
From: Paul Sliwowski <psliwowski@google.com>
Date: Mon, 24 Jun 2013 19:11:23 -0700
Subject: [PATCH] Fixed issue where we vibrate even when System setting has it
 disabled.

Bug: 8833434
Change-Id: Ie5e8ddf348463d5932c735b4427a4c69f0da273d
(cherry picked from commit 29f325e9f0b5a84b36b0bf4d6ab38bc4867d6b5c)
---
 .../HapticFeedbackController.java             | 74 +++++++++++++++++++
 .../datetimepicker/date/DatePickerDialog.java | 40 +++++-----
 .../time/RadialPickerLayout.java              | 36 ++-------
 .../datetimepicker/time/TimePickerDialog.java | 31 ++++++--
 4 files changed, 125 insertions(+), 56 deletions(-)
 create mode 100644 src/com/android/datetimepicker/HapticFeedbackController.java

diff --git a/src/com/android/datetimepicker/HapticFeedbackController.java b/src/com/android/datetimepicker/HapticFeedbackController.java
new file mode 100644
index 0000000..b9be63f
--- /dev/null
+++ b/src/com/android/datetimepicker/HapticFeedbackController.java
@@ -0,0 +1,74 @@
+package com.android.datetimepicker;
+
+import android.app.Service;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.os.Vibrator;
+import android.provider.Settings;
+
+/**
+ * A simple utility class to handle haptic feedback.
+ */
+public class HapticFeedbackController {
+    private static final int VIBRATE_DELAY_MS = 125;
+    private static final int VIBRATE_LENGTH_MS = 5;
+
+    private static boolean checkGlobalSetting(Context context) {
+        return Settings.System.getInt(context.getContentResolver(),
+                Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) == 1;
+    }
+
+    private final Context mContext;
+    private final ContentObserver mContentObserver;
+
+    private Vibrator mVibrator;
+    private boolean mIsGloballyEnabled;
+    private long mLastVibrate;
+
+    public HapticFeedbackController(Context context) {
+        mContext = context;
+        mContentObserver = new ContentObserver(null) {
+            @Override
+            public void onChange(boolean selfChange) {
+                mIsGloballyEnabled = checkGlobalSetting(mContext);
+            }
+        };
+    }
+
+    /**
+     * Call to setup the controller.
+     */
+    public void start() {
+        mVibrator = (Vibrator) mContext.getSystemService(Service.VIBRATOR_SERVICE);
+
+        // Setup a listener for changes in haptic feedback settings
+        mIsGloballyEnabled = checkGlobalSetting(mContext);
+        Uri uri = Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_ENABLED);
+        mContext.getContentResolver().registerContentObserver(uri, false, mContentObserver);
+    }
+
+    /**
+     * Call this when you don't need the controller anymore.
+     */
+    public void stop() {
+        mVibrator = null;
+        mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+    }
+
+    /**
+     * Try to vibrate. To prevent this becoming a single continuous vibration, nothing will
+     * happen if we have vibrated very recently.
+     */
+    public void tryVibrate() {
+        if (mVibrator != null && mIsGloballyEnabled) {
+            long now = SystemClock.uptimeMillis();
+            // We want to try to vibrate each individual tick discretely.
+            if (now - mLastVibrate >= VIBRATE_DELAY_MS) {
+                mVibrator.vibrate(VIBRATE_LENGTH_MS);
+                mLastVibrate = now;
+            }
+        }
+    }
+}
diff --git a/src/com/android/datetimepicker/date/DatePickerDialog.java b/src/com/android/datetimepicker/date/DatePickerDialog.java
index 84ef2f3..d16eedd 100644
--- a/src/com/android/datetimepicker/date/DatePickerDialog.java
+++ b/src/com/android/datetimepicker/date/DatePickerDialog.java
@@ -19,15 +19,9 @@ package com.android.datetimepicker.date;
 import android.animation.ObjectAnimator;
 import android.app.Activity;
 import android.app.DialogFragment;
-import android.content.Context;
 import android.content.res.Resources;
 import android.os.Bundle;
-import android.os.SystemClock;
-import android.os.Vibrator;
-import android.text.format.DateFormat;
 import android.text.format.DateUtils;
-import android.text.format.Time;
-import android.util.AttributeSet;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -35,14 +29,13 @@ import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-import android.widget.ViewAnimator;
 
+import com.android.datetimepicker.HapticFeedbackController;
 import com.android.datetimepicker.R;
 import com.android.datetimepicker.Utils;
 import com.android.datetimepicker.date.SimpleMonthAdapter.CalendarDay;
@@ -105,8 +98,7 @@ public class DatePickerDialog extends DialogFragment implements
     private int mMinYear = DEFAULT_START_YEAR;
     private int mMaxYear = DEFAULT_END_YEAR;
 
-    private Vibrator mVibrator;
-    private long mLastVibrate;
+    private HapticFeedbackController mHapticFeedbackController;
 
     private boolean mDelayAnimation = true;
 
@@ -171,7 +163,6 @@ public class DatePickerDialog extends DialogFragment implements
         final Activity activity = getActivity();
         activity.getWindow().setSoftInputMode(
                 WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
-        mVibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);
         if (savedInstanceState != null) {
             mCalendar.set(Calendar.YEAR, savedInstanceState.getInt(KEY_SELECTED_YEAR));
             mCalendar.set(Calendar.MONTH, savedInstanceState.getInt(KEY_SELECTED_MONTH));
@@ -274,9 +265,23 @@ public class DatePickerDialog extends DialogFragment implements
                 mYearPickerView.postSetSelectionFromTop(listPosition, listPositionOffset);
             }
         }
+
+        mHapticFeedbackController = new HapticFeedbackController(activity);
         return view;
     }
 
+    @Override
+    public void onResume() {
+        super.onResume();
+        mHapticFeedbackController.start();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mHapticFeedbackController.stop();
+    }
+
     private void setCurrentView(final int viewIndex) {
         long millis = mCalendar.getTimeInMillis();
 
@@ -453,19 +458,8 @@ public class DatePickerDialog extends DialogFragment implements
         mListeners.remove(listener);
     }
 
-    /**
-     * Try to vibrate. To prevent this becoming a single continuous vibration, nothing will
-     * happen if we have vibrated very recently.
-     */
     @Override
     public void tryVibrate() {
-        if (mVibrator != null) {
-            long now = SystemClock.uptimeMillis();
-            // We want to try to vibrate each individual tick discretely.
-            if (now - mLastVibrate >= 125) {
-                mVibrator.vibrate(5);
-                mLastVibrate = now;
-            }
-        }
+        mHapticFeedbackController.tryVibrate();
     }
 }
diff --git a/src/com/android/datetimepicker/time/RadialPickerLayout.java b/src/com/android/datetimepicker/time/RadialPickerLayout.java
index e895e98..bad77ed 100644
--- a/src/com/android/datetimepicker/time/RadialPickerLayout.java
+++ b/src/com/android/datetimepicker/time/RadialPickerLayout.java
@@ -19,13 +19,11 @@ package com.android.datetimepicker.time;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.annotation.SuppressLint;
-import android.app.Service;
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.SystemClock;
-import android.os.Vibrator;
 import android.text.format.DateUtils;
 import android.text.format.Time;
 import android.util.AttributeSet;
@@ -42,8 +40,6 @@ import android.widget.FrameLayout;
 
 import com.android.datetimepicker.R;
 
-import java.util.HashMap;
-
 public class RadialPickerLayout extends FrameLayout implements OnTouchListener {
     private static final String TAG = "RadialPickerLayout";
 
@@ -60,10 +56,9 @@ public class RadialPickerLayout extends FrameLayout implements OnTouchListener {
     private static final int AM = TimePickerDialog.AM;
     private static final int PM = TimePickerDialog.PM;
 
-    private Vibrator mVibrator;
-    private long mLastVibrate;
     private int mLastValueSelected;
 
+    private TimePickerDialog mController;
     private OnValueSelectedListener mListener;
     private boolean mTimeInitialized;
     private int mCurrentHoursOfDay;
@@ -125,8 +120,6 @@ public class RadialPickerLayout extends FrameLayout implements OnTouchListener {
         // Prepare mapping to snap touchable degrees to selectable degrees.
         preparePrefer30sMap();
 
-        mVibrator = (Vibrator) context.getSystemService(Service.VIBRATOR_SERVICE);
-        mLastVibrate = 0;
         mLastValueSelected = -1;
 
         mInputEnabled = true;
@@ -168,12 +161,14 @@ public class RadialPickerLayout extends FrameLayout implements OnTouchListener {
      * @param initialMinutes
      * @param is24HourMode
      */
-    public void initialize(Context context, int initialHoursOfDay, int initialMinutes,
-            boolean is24HourMode) {
+    public void initialize(Context context, TimePickerDialog controller,
+            int initialHoursOfDay, int initialMinutes, boolean is24HourMode) {
         if (mTimeInitialized) {
             Log.e(TAG, "Time has already been initialized.");
             return;
         }
+
+        mController = controller;
         mIs24HourMode = is24HourMode;
         mHideAmPm = mAccessibilityManager.isTouchExplorationEnabled()? true : mIs24HourMode;
 
@@ -582,7 +577,7 @@ public class RadialPickerLayout extends FrameLayout implements OnTouchListener {
                 if (mIsTouchingAmOrPm == AM || mIsTouchingAmOrPm == PM) {
                     // If the touch is on AM or PM, set it as "touched" after the TAP_TIMEOUT
                     // in case the user moves their finger quickly.
-                    tryVibrate();
+                    mController.tryVibrate();
                     mDownDegrees = -1;
                     mHandler.postDelayed(new Runnable() {
                         @Override
@@ -600,7 +595,7 @@ public class RadialPickerLayout extends FrameLayout implements OnTouchListener {
                     if (mDownDegrees != -1) {
                         // If it's a legal touch, set that number as "selected" after the
                         // TAP_TIMEOUT in case the user moves their finger quickly.
-                        tryVibrate();
+                        mController.tryVibrate();
                         mHandler.postDelayed(new Runnable() {
                             @Override
                             public void run() {
@@ -655,7 +650,7 @@ public class RadialPickerLayout extends FrameLayout implements OnTouchListener {
                 if (degrees != -1) {
                     value = reselectSelector(degrees, isInnerCircle[0], false, true);
                     if (value != mLastValueSelected) {
-                        tryVibrate();
+                        mController.tryVibrate();
                         mLastValueSelected = value;
                         mListener.onValueSelected(getCurrentItemShowing(), value, false);
                     }
@@ -714,21 +709,6 @@ public class RadialPickerLayout extends FrameLayout implements OnTouchListener {
         return false;
     }
 
-    /**
-     * Try to vibrate. To prevent this becoming a single continuous vibration, nothing will
-     * happen if we have vibrated very recently.
-     */
-    public void tryVibrate() {
-        if (mVibrator != null) {
-            long now = SystemClock.uptimeMillis();
-            // We want to try to vibrate each individual tick discretely.
-            if (now - mLastVibrate >= 125) {
-                mVibrator.vibrate(5);
-                mLastVibrate = now;
-            }
-        }
-    }
-
     /**
      * Set touch input as enabled or disabled, for use with keyboard mode.
      */
diff --git a/src/com/android/datetimepicker/time/TimePickerDialog.java b/src/com/android/datetimepicker/time/TimePickerDialog.java
index d5db83b..89aaf8e 100644
--- a/src/com/android/datetimepicker/time/TimePickerDialog.java
+++ b/src/com/android/datetimepicker/time/TimePickerDialog.java
@@ -34,6 +34,7 @@ import android.view.Window;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import com.android.datetimepicker.HapticFeedbackController;
 import com.android.datetimepicker.R;
 import com.android.datetimepicker.Utils;
 import com.android.datetimepicker.time.RadialPickerLayout.OnValueSelectedListener;
@@ -69,6 +70,8 @@ public class TimePickerDialog extends DialogFragment implements OnValueSelectedL
 
     private OnTimeSetListener mCallback;
 
+    private HapticFeedbackController mHapticFeedbackController;
+
     private TextView mDoneButton;
     private TextView mHourView;
     private TextView mHourSpaceView;
@@ -199,7 +202,8 @@ public class TimePickerDialog extends DialogFragment implements OnValueSelectedL
         mTimePicker = (RadialPickerLayout) view.findViewById(R.id.time_picker);
         mTimePicker.setOnValueSelectedListener(this);
         mTimePicker.setOnKeyListener(keyboardListener);
-        mTimePicker.initialize(getActivity(), mInitialHourOfDay, mInitialMinute, mIs24HourMode);
+        mTimePicker.initialize(getActivity(), this, mInitialHourOfDay, mInitialMinute,
+                mIs24HourMode);
         int currentItemShowing = HOUR_INDEX;
         if (savedInstanceState != null &&
                 savedInstanceState.containsKey(KEY_CURRENT_ITEM_SHOWING)) {
@@ -208,18 +212,19 @@ public class TimePickerDialog extends DialogFragment implements OnValueSelectedL
         setCurrentItemShowing(currentItemShowing, false, true, true);
         mTimePicker.invalidate();
 
+        mHapticFeedbackController = new HapticFeedbackController(getActivity());
         mHourView.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
                 setCurrentItemShowing(HOUR_INDEX, true, false, true);
-                mTimePicker.tryVibrate();
+                tryVibrate();
             }
         });
         mMinuteView.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
                 setCurrentItemShowing(MINUTE_INDEX, true, false, true);
-                mTimePicker.tryVibrate();
+                tryVibrate();
             }
         });
 
@@ -230,7 +235,7 @@ public class TimePickerDialog extends DialogFragment implements OnValueSelectedL
                 if (mInKbMode && isTypedTimeFullyLegal()) {
                     finishKbMode(false);
                 } else {
-                    mTimePicker.tryVibrate();
+                    tryVibrate();
                 }
                 if (mCallback != null) {
                     mCallback.onTimeSet(mTimePicker,
@@ -257,7 +262,7 @@ public class TimePickerDialog extends DialogFragment implements OnValueSelectedL
             mAmPmHitspace.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
-                    mTimePicker.tryVibrate();
+                    tryVibrate();
                     int amOrPm = mTimePicker.getIsCurrentlyAmOrPm();
                     if (amOrPm == AM) {
                         amOrPm = PM;
@@ -291,6 +296,22 @@ public class TimePickerDialog extends DialogFragment implements OnValueSelectedL
         return view;
     }
 
+    @Override
+    public void onResume() {
+        super.onResume();
+        mHapticFeedbackController.start();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mHapticFeedbackController.stop();
+    }
+
+    public void tryVibrate() {
+        mHapticFeedbackController.tryVibrate();
+    }
+
     private void updateAmPmDisplay(int amOrPm) {
         if (amOrPm == AM) {
             mAmPmTextView.setText(mAmText);
-- 
GitLab