summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChiao Cheng <chiaocheng@google.com>2012-08-23 14:08:35 -0700
committerChiao Cheng <chiaocheng@google.com>2012-08-24 14:28:37 -0700
commit0ada29ea0983ed27e33d12dfe9b1280fc7005a7e (patch)
tree34c7ee1339d359b006495f9684ace197d93dd443
parentfaed97c4fb2fade78ca0e7efcfa88d14dfe46175 (diff)
downloadandroid_packages_apps_PhoneCommon-0ada29ea0983ed27e33d12dfe9b1280fc7005a7e.tar.gz
android_packages_apps_PhoneCommon-0ada29ea0983ed27e33d12dfe9b1280fc7005a7e.tar.bz2
android_packages_apps_PhoneCommon-0ada29ea0983ed27e33d12dfe9b1280fc7005a7e.zip
Adding classes that were part of com.android.phone.common.
Bug: 7054788 Change-Id: If26c76fc79d5b8cba543d69869c48faca9614e2b
-rw-r--r--Android.mk4
-rw-r--r--src/com/android/phone/common/CallLogAsync.java233
-rw-r--r--src/com/android/phone/common/HapticFeedback.java171
3 files changed, 405 insertions, 3 deletions
diff --git a/Android.mk b/Android.mk
index 62cf6fc..f01263e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -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;
+ }
+}