diff options
author | Chiao Cheng <chiaocheng@google.com> | 2012-08-23 14:08:35 -0700 |
---|---|---|
committer | Chiao Cheng <chiaocheng@google.com> | 2012-08-24 14:28:37 -0700 |
commit | 0ada29ea0983ed27e33d12dfe9b1280fc7005a7e (patch) | |
tree | 34c7ee1339d359b006495f9684ace197d93dd443 | |
parent | faed97c4fb2fade78ca0e7efcfa88d14dfe46175 (diff) | |
download | packages_apps_PhoneCommon-0ada29ea0983ed27e33d12dfe9b1280fc7005a7e.tar.gz packages_apps_PhoneCommon-0ada29ea0983ed27e33d12dfe9b1280fc7005a7e.tar.bz2 packages_apps_PhoneCommon-0ada29ea0983ed27e33d12dfe9b1280fc7005a7e.zip |
Adding classes that were part of com.android.phone.common.
Bug: 7054788
Change-Id: If26c76fc79d5b8cba543d69869c48faca9614e2b
-rw-r--r-- | Android.mk | 4 | ||||
-rw-r--r-- | src/com/android/phone/common/CallLogAsync.java | 233 | ||||
-rw-r--r-- | src/com/android/phone/common/HapticFeedback.java | 171 |
3 files changed, 405 insertions, 3 deletions
@@ -14,12 +14,10 @@ LOCAL_PATH:= $(call my-dir) -# Static library with some common classes for the phone apps. -# To use it add this line in your Android.mk -# LOCAL_STATIC_JAVA_LIBRARIES := com.android.phone.common include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_MODULE := com.android.phone.shared + include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/src/com/android/phone/common/CallLogAsync.java b/src/com/android/phone/common/CallLogAsync.java new file mode 100644 index 0000000..e8ed6b6 --- /dev/null +++ b/src/com/android/phone/common/CallLogAsync.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2010 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.phone.common; +import android.content.Context; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Looper; +import android.provider.CallLog.Calls; +import android.util.Log; +import com.android.internal.telephony.CallerInfo; + +/** + * Class to access the call logs database asynchronously since + * database ops can take a long time depending on the system's load. + * It uses AsyncTask which has its own thread pool. + * + * <pre class="prettyprint"> + * Typical usage: + * ============== + * + * // From an activity... + * String mLastNumber = ""; + * + * CallLogAsync log = new CallLogAsync(); + * + * CallLogAsync.AddCallArgs addCallArgs = new CallLogAsync.AddCallArgs( + * this, ci, number, presentation, type, timestamp, duration); + * + * log.addCall(addCallArgs); + * + * CallLogAsync.GetLastOutgoingCallArgs lastCallArgs = new CallLogAsync.GetLastOutgoingCallArgs( + * this, new CallLogAsync.OnLastOutgoingCallComplete() { + * public void lastOutgoingCall(String number) { mLastNumber = number; } + * }); + * log.getLastOutgoingCall(lastCallArgs); + * </pre> + * + */ + +public class CallLogAsync { + private static final String TAG = "CallLogAsync"; + + /** + * Parameter object to hold the args to add a call in the call log DB. + */ + public static class AddCallArgs { + /** + * @param ci CallerInfo. + * @param number To be logged. + * @param presentation Of the number. + * @param callType The type of call (e.g INCOMING_TYPE). @see + * android.provider.CallLog for the list of values. + * @param timestamp Of the call (millisecond since epoch). + * @param durationInMillis Of the call (millisecond). + */ + public AddCallArgs(Context context, + CallerInfo ci, + String number, + int presentation, + int callType, + long timestamp, + long durationInMillis) { + // Note that the context is passed each time. We could + // have stored it in a member but we've run into a bunch + // of memory leaks in the past that resulted from storing + // references to contexts in places that were long lived + // when the contexts were expected to be short lived. For + // example, if you initialize this class with an Activity + // instead of an Application the Activity can't be GCed + // until this class can, and Activities tend to hold + // references to large amounts of RAM for things like the + // bitmaps in their views. + // + // Having hit more than a few of those bugs in the past + // we've grown cautious of storing references to Contexts + // when it's not very clear that the thing holding the + // references is tightly tied to the Context, for example + // Views the Activity is displaying. + + this.context = context; + this.ci = ci; + this.number = number; + this.presentation = presentation; + this.callType = callType; + this.timestamp = timestamp; + this.durationInSec = (int)(durationInMillis / 1000); + } + // Since the members are accessed directly, we don't use the + // mXxxx notation. + public final Context context; + public final CallerInfo ci; + public final String number; + public final int presentation; + public final int callType; + public final long timestamp; + public final int durationInSec; + } + + /** + * Parameter object to hold the args to get the last outgoing call + * from the call log DB. + */ + public static class GetLastOutgoingCallArgs { + public GetLastOutgoingCallArgs(Context context, + OnLastOutgoingCallComplete callback) { + this.context = context; + this.callback = callback; + } + public final Context context; + public final OnLastOutgoingCallComplete callback; + } + + /** + * Non blocking version of CallLog.addCall(...) + */ + public AsyncTask addCall(AddCallArgs args) { + assertUiThread(); + return new AddCallTask().execute(args); + } + + /** Interface to retrieve the last dialed number asynchronously. */ + public interface OnLastOutgoingCallComplete { + /** @param number The last dialed number or an empty string if + * none exists yet. */ + void lastOutgoingCall(String number); + } + + /** + * CallLog.getLastOutgoingCall(...) + */ + public AsyncTask getLastOutgoingCall(GetLastOutgoingCallArgs args) { + assertUiThread(); + return new GetLastOutgoingCallTask(args.callback).execute(args); + } + + /** + * AsyncTask to save calls in the DB. + */ + private class AddCallTask extends AsyncTask<AddCallArgs, Void, Uri[]> { + @Override + protected Uri[] doInBackground(AddCallArgs... callList) { + int count = callList.length; + Uri[] result = new Uri[count]; + for (int i = 0; i < count; i++) { + AddCallArgs c = callList[i]; + + try { + // May block. + result[i] = Calls.addCall( + c.ci, c.context, c.number, c.presentation, + c.callType, c.timestamp, c.durationInSec); + } catch (Exception e) { + // This must be very rare but may happen in legitimate cases. + // e.g. If the phone is encrypted and thus write request fails, it may + // cause some kind of Exception (right now it is IllegalArgumentException, but + // might change). + // + // We don't want to crash the whole process just because of that. + // Let's just ignore it and leave logs instead. + Log.e(TAG, "Exception raised during adding CallLog entry: " + e); + result[i] = null; + } + } + return result; + } + + // Perform a simple sanity check to make sure the call was + // written in the database. Typically there is only one result + // per call so it is easy to identify which one failed. + @Override + protected void onPostExecute(Uri[] result) { + for (Uri uri : result) { + if (uri == null) { + Log.e(TAG, "Failed to write call to the log."); + } + } + } + } + + /** + * AsyncTask to get the last outgoing call from the DB. + */ + private class GetLastOutgoingCallTask extends AsyncTask<GetLastOutgoingCallArgs, Void, String> { + private final OnLastOutgoingCallComplete mCallback; + private String mNumber; + public GetLastOutgoingCallTask(OnLastOutgoingCallComplete callback) { + mCallback = callback; + } + + // Happens on a background thread. We cannot run the callback + // here because only the UI thread can modify the view + // hierarchy (e.g enable/disable the dial button). The + // callback is ran rom the post execute method. + @Override + protected String doInBackground(GetLastOutgoingCallArgs... list) { + int count = list.length; + String number = ""; + for (GetLastOutgoingCallArgs args : list) { + // May block. Select only the last one. + number = Calls.getLastOutgoingCall(args.context); + } + return number; // passed to the onPostExecute method. + } + + // Happens on the UI thread, it is safe to run the callback + // that may do some work on the views. + @Override + protected void onPostExecute(String number) { + assertUiThread(); + mCallback.lastOutgoingCall(number); + } + } + + private void assertUiThread() { + if (!Looper.getMainLooper().equals(Looper.myLooper())) { + throw new RuntimeException("Not on the UI thread!"); + } + } +} diff --git a/src/com/android/phone/common/HapticFeedback.java b/src/com/android/phone/common/HapticFeedback.java new file mode 100644 index 0000000..c470730 --- /dev/null +++ b/src/com/android/phone/common/HapticFeedback.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2009 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.phone.common; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources; +import android.os.SystemVibrator; +import android.os.Vibrator; +import android.provider.Settings; +import android.provider.Settings.System; +import android.util.Log; + +/** + * Handles the haptic feedback: a light buzz happening when the user + * presses a soft key (UI button or capacitive key). The haptic + * feedback is controlled by: + * - a system resource for the pattern + * The pattern used is tuned per device and stored in an internal + * resource (config_virtualKeyVibePattern.) + * - a system setting HAPTIC_FEEDBACK_ENABLED. + * HAPTIC_FEEDBACK_ENABLED can be changed by the user using the + * system Settings activity. It must be rechecked each time the + * activity comes in the foreground (onResume). + * + * This class is not thread safe. It assumes it'll be called from the + * UI thead. + * + * Typical usage: + * -------------- + * static private final boolean HAPTIC_ENABLED = true; + * private HapticFeedback mHaptic = new HapticFeedback(); + * + * protected void onCreate(Bundle icicle) { + * mHaptic.init((Context)this, HAPTIC_ENABLED); + * } + * + * protected void onResume() { + * // Refresh the system setting. + * mHaptic.checkSystemSetting(); + * } + * + * public void foo() { + * mHaptic.vibrate(); + * } + * + */ + +public class HapticFeedback { + private static final int VIBRATION_PATTERN_ID = + com.android.internal.R.array.config_virtualKeyVibePattern; + /** If no pattern was found, vibrate for a small amount of time. */ + private static final long DURATION = 10; // millisec. + /** Play the haptic pattern only once. */ + private static final int NO_REPEAT = -1; + + private static final String TAG = "HapticFeedback"; + private Context mContext; + private long[] mHapticPattern; + private Vibrator mVibrator; + + private boolean mEnabled; + private Settings.System mSystemSettings; + private ContentResolver mContentResolver; + private boolean mSettingEnabled; + + /** + * Initialize this instance using the app and system + * configs. Since these don't change, init is typically called + * once in 'onCreate'. + * checkSettings is not called during init. + * @param context To look up the resources and system settings. + * @param enabled If false, vibrate will be a no-op regardless of + * the system settings. + */ + public void init(Context context, boolean enabled) { + mEnabled = enabled; + if (enabled) { + // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this + // vibrator object will be isolated from others. + mVibrator = new SystemVibrator(); + if (!loadHapticSystemPattern(context.getResources())) { + mHapticPattern = new long[] {0, DURATION, 2 * DURATION, 3 * DURATION}; + } + mSystemSettings = new Settings.System(); + mContentResolver = context.getContentResolver(); + } + } + + + /** + * Reload the system settings to check if the user enabled the + * haptic feedback. + */ + public void checkSystemSetting() { + if (!mEnabled) { + return; + } + try { + int val = mSystemSettings.getInt(mContentResolver, System.HAPTIC_FEEDBACK_ENABLED, 0); + mSettingEnabled = val != 0; + } catch (Resources.NotFoundException nfe) { + Log.e(TAG, "Could not retrieve system setting.", nfe); + mSettingEnabled = false; + } + + } + + + /** + * Generate the haptic feedback vibration. Only one thread can + * request it. If the phone is already in a middle of an haptic + * feedback sequence, the request is ignored. + */ + public void vibrate() { + if (!mEnabled || !mSettingEnabled) { + return; + } + // System-wide configuration may return different styles of haptic feedback pattern. + // - an array with one value implies "one-shot vibration" + // - an array with multiple values implies "pattern vibration" + // We need to switch methods to call depending on the difference. + // See also PhoneWindowManager#performHapticFeedbackLw() for another example. + if (mHapticPattern != null && mHapticPattern.length == 1) { + mVibrator.vibrate(mHapticPattern[0]); + } else { + mVibrator.vibrate(mHapticPattern, NO_REPEAT); + } + } + + /** + * @return true If the system haptic pattern was found. + */ + private boolean loadHapticSystemPattern(Resources r) { + int[] pattern; + + mHapticPattern = null; + try { + pattern = r.getIntArray(VIBRATION_PATTERN_ID); + } catch (Resources.NotFoundException nfe) { + Log.e(TAG, "Vibrate pattern missing.", nfe); + return false; + } + + if (null == pattern || pattern.length == 0) { + Log.e(TAG, "Haptic pattern is null or empty."); + return false; + } + + // int[] to long[] conversion. + mHapticPattern = new long[pattern.length]; + for (int i = 0; i < pattern.length; i++) { + mHapticPattern[i] = pattern[i]; + } + return true; + } +} |