P2pEventManager.java revision 3a596230af0e497ef5980441818fb1a20bacc74c
1/*
2 * Copyright (C) 2011 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
17package com.android.nfc;
18
19import android.app.Notification;
20import android.app.NotificationManager;
21import android.app.PendingIntent;
22import android.content.Context;
23import android.content.Intent;
24import android.content.SharedPreferences;
25import android.media.AudioManager;
26import android.media.SoundPool;
27import android.os.Vibrator;
28import android.provider.Settings;
29import android.view.OrientationEventListener;
30
31import com.android.nfc3.R;
32
33/**
34 * Manages vibration, sound and animation for P2P events.
35 */
36public class P2pEventManager implements P2pEventListener {
37    static final String TAG = "NfcP2pEventManager";
38    static final boolean DBG = true;
39
40    static final String PREF_FIRST_SHARE = "first_share";
41    static final int NOTIFICATION_FIRST_SHARE = 0;
42
43    static final long[] VIBRATION_PATTERN = {0, 100, 10000};
44
45    final Context mContext;
46    final P2pEventListener.Callback mCallback;
47    final SharedPreferences mPrefs;
48    final int mStartSound;
49    final int mEndSound;
50    final int mErrorSound;
51    final SoundPool mSoundPool; // playback synchronized on this
52    final Vibrator mVibrator;
53    final RotationDetector mRotationDetector;
54    final NotificationManager mNotificationManager;
55
56    boolean mAnimating;
57    boolean mPrefsFirstShare;
58
59    class RotationDetector extends OrientationEventListener {
60        static final int THRESHOLD_DEGREES = 35;
61        int mStartOrientation;
62        boolean mRotateDetectionEnabled;
63
64        public RotationDetector(Context context) {
65            super(context);
66        }
67        public void start() {
68            synchronized (RotationDetector.this) {
69                mRotateDetectionEnabled = true;
70                mStartOrientation = -1;
71                super.enable();
72            }
73        }
74        public void cancel() {
75            synchronized (RotationDetector.this) {
76                mRotateDetectionEnabled = false;
77                super.disable();
78            }
79        }
80        @Override
81        public void onOrientationChanged(int orientation) {
82            synchronized (RotationDetector.this) {
83                if (!mRotateDetectionEnabled) {
84                    return;
85                }
86                if (mStartOrientation < 0) {
87                    mStartOrientation = orientation;
88                }
89                int diff = Math.abs(mStartOrientation - orientation);
90                if (diff > THRESHOLD_DEGREES && diff < (360-THRESHOLD_DEGREES)) {
91                    cancel();
92                    mVibrator.vibrate(VIBRATION_PATTERN, -1);
93                    mCallback.onP2pSendConfirmed();
94                }
95            }
96        }
97    }
98
99    public P2pEventManager(Context context, P2pEventListener.Callback callback) {
100        mContext = context;
101        mCallback = callback;
102        mRotationDetector = new RotationDetector(mContext);
103        mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
104        mStartSound = mSoundPool.load(mContext, R.raw.start, 1);
105        mEndSound = mSoundPool.load(mContext, R.raw.end, 1);
106        mErrorSound = mSoundPool.load(mContext, R.raw.error, 1);
107        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
108        mNotificationManager = (NotificationManager) mContext.getSystemService(
109                Context.NOTIFICATION_SERVICE);
110
111        SharedPreferences prefs = mContext.getSharedPreferences(NfcService.PREF,
112                Context.MODE_PRIVATE);
113        if (prefs.getBoolean(PREF_FIRST_SHARE, true)) {
114            mPrefs = prefs;
115            mPrefsFirstShare = true;
116        } else {
117            // don't need to check pref again
118            mPrefs = null;
119            mPrefsFirstShare = false;
120        }
121
122        mAnimating = false;
123    }
124
125    @Override
126    public void onP2pInRange() {
127        mVibrator.vibrate(VIBRATION_PATTERN, -1);
128        playSound(mStartSound);
129    }
130
131    @Override
132    public void onP2pSendConfirmationRequested() {
133        mRotationDetector.start();
134        P2pAnimationActivity.makeScreenshot(mContext);
135        P2pAnimationActivity.setCallback(mCallback);
136        mAnimating = true;
137        // Start the animation activity
138        Intent animIntent = new Intent();
139        animIntent.setClass(mContext, P2pAnimationActivity.class);
140        animIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NEW_TASK);
141        mContext.startActivity(animIntent);
142    }
143
144    @Override
145    public void onP2pSendComplete(boolean result) {
146        if (!result) {
147            playSound(mErrorSound);
148        } else {
149            playSound(mEndSound);
150            checkFirstShare();
151        }
152        finish(result, false);
153    }
154
155    @Override
156    public void onP2pReceiveComplete() {
157        mRotationDetector.cancel();
158        finish(false, true);
159    }
160
161    @Override
162    public void onP2pOutOfRange() {
163        mRotationDetector.cancel();
164        if (mAnimating) {
165            finish(false, false);
166            playSound(mErrorSound);
167        }
168    }
169
170    /**
171     * Finish up the animation, if running, and play ending sounds.
172     * Must be called on the UI thread.
173     */
174    void finish(boolean sendSuccess, boolean receiveSuccess) {
175        if (sendSuccess) {
176            if (mAnimating) {
177                P2pAnimationActivity.finishWithSend();
178            }
179        } else if (receiveSuccess) {
180            if (mAnimating) {
181                P2pAnimationActivity.finishWithReceive();
182            }
183        } else {
184            if (mAnimating) {
185                P2pAnimationActivity.finishWithFailure();
186            }
187        }
188        mAnimating = false;
189    }
190
191    /** If first time, display up a notification */
192    void checkFirstShare() {
193        synchronized (this) {
194            if (mPrefsFirstShare) {
195                mPrefsFirstShare = false;
196                SharedPreferences.Editor editor = mPrefs.edit();
197                editor.putBoolean(PREF_FIRST_SHARE, false);
198                editor.apply();
199
200                Intent intent = new Intent(Settings.ACTION_NFCSHARING_SETTINGS);
201                PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent,
202                        PendingIntent.FLAG_UPDATE_CURRENT);
203                Notification notification = new Notification.Builder(mContext)
204                        .setContentTitle(mContext.getString(R.string.first_share_title))
205                        .setContentText(mContext.getString(R.string.first_share_text))
206                        .setContentIntent(pi)
207                        .setSmallIcon(R.drawable.stat_sys_nfc)
208                        .setAutoCancel(true)
209                        .getNotification();
210                mNotificationManager.notify(NOTIFICATION_FIRST_SHARE, notification);
211            }
212        }
213    }
214
215    void playSound(int sound) {
216        synchronized (this) {
217            mSoundPool.play(sound, 1.0f, 1.0f, 0, 0, 1.0f);
218        }
219    }
220}
221