summaryrefslogtreecommitdiff
path: root/src/com/android/dialer
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/dialer')
-rw-r--r--src/com/android/dialer/CallDetailActivity.java10
-rw-r--r--src/com/android/dialer/DialtactsActivity.java29
-rw-r--r--src/com/android/dialer/calllog/CallLogActivity.java31
-rw-r--r--src/com/android/dialer/calllog/CallLogFragment.java4
-rw-r--r--src/com/android/dialer/calllog/CallLogListItemViewHolder.java37
-rw-r--r--src/com/android/dialer/database/DialerDatabaseHelper.java6
-rw-r--r--src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java54
-rw-r--r--src/com/android/dialer/database/FilteredNumberContract.java13
-rw-r--r--src/com/android/dialer/database/FilteredNumberProvider.java25
-rw-r--r--src/com/android/dialer/dialpad/DialpadFragment.java9
-rw-r--r--src/com/android/dialer/filterednumber/BlockedNumberAdapter.java148
-rw-r--r--src/com/android/dialer/filterednumber/BlockedNumberFragment.java154
-rw-r--r--src/com/android/dialer/filterednumber/FilterNumberDialogFragment.java15
-rw-r--r--src/com/android/dialer/list/RegularSearchFragment.java2
-rw-r--r--src/com/android/dialer/settings/DialerSettingsActivity.java11
-rw-r--r--src/com/android/dialer/util/DialerUtils.java15
-rw-r--r--src/com/android/dialer/util/PhoneNumberUtil.java43
-rw-r--r--src/com/android/dialer/util/TelecomUtil.java27
-rw-r--r--src/com/android/dialer/voicemail/VoicemailAudioManager.java140
-rw-r--r--src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java27
-rw-r--r--src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java83
-rw-r--r--src/com/android/dialer/voicemail/WiredHeadsetManager.java88
22 files changed, 863 insertions, 108 deletions
diff --git a/src/com/android/dialer/CallDetailActivity.java b/src/com/android/dialer/CallDetailActivity.java
index 72a50127e..d95b55ecb 100644
--- a/src/com/android/dialer/CallDetailActivity.java
+++ b/src/com/android/dialer/CallDetailActivity.java
@@ -31,6 +31,7 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;
import android.widget.QuickContactBadge;
@@ -39,6 +40,7 @@ import android.widget.Toast;
import com.android.contacts.common.ContactPhotoManager;
import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.interactions.TouchPointManager;
import com.android.contacts.common.GeoUtil;
import com.android.contacts.common.CallUtil;
import com.android.contacts.common.util.UriUtils;
@@ -262,6 +264,14 @@ public class CallDetailActivity extends AppCompatActivity
getCallDetails();
}
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ TouchPointManager.getInstance().setPoint((int) ev.getRawX(), (int) ev.getRawY());
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+
public void getCallDetails() {
CallLogAsyncTaskUtil.getCallDetails(this, getCallLogEntryUris(), mCallLogAsyncTaskListener);
}
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index dbff276e5..f8fb17f6b 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -83,6 +83,7 @@ import com.android.dialer.list.SmartDialSearchFragment;
import com.android.dialer.list.SpeedDialFragment;
import com.android.dialer.settings.DialerSettingsActivity;
import com.android.dialer.util.IntentUtil;
+import com.android.dialer.util.TelecomUtil;
import com.android.dialer.util.IntentUtil.CallIntentBuilder;
import com.android.dialer.util.DialerUtils;
import com.android.dialer.widget.ActionBarController;
@@ -158,6 +159,8 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
*/
private SmartDialSearchFragment mSmartDialSearchFragment;
+ private boolean mIsVisible;
+
/**
* Animation that slides in.
*/
@@ -563,6 +566,25 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
}
@Override
+ protected void onStart() {
+ super.onStart();
+ mIsVisible = true;
+ }
+
+ @Override
+ protected void onStop() {
+ mIsVisible = false;
+ super.onStop();
+ }
+
+ /**
+ * Returns true when the Activity is currently visible (between onStart and onStop).
+ */
+ /* package */ boolean isVisible() {
+ return mIsVisible;
+ }
+
+ @Override
protected void onPause() {
if (mClearSearchOnPause) {
hideDialpadAndSearchUi();
@@ -576,6 +598,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
@Override
protected void onSaveInstanceState(Bundle outState) {
+ mIsVisible = false;
super.onSaveInstanceState(outState);
outState.putString(KEY_SEARCH_QUERY, mSearchQuery);
outState.putBoolean(KEY_IN_REGULAR_SEARCH_UI, mInRegularSearch);
@@ -648,6 +671,10 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
@Override
public boolean onMenuItemClick(MenuItem item) {
+ if (!isVisible()) {
+ return true;
+ }
+
switch (item.getItemId()) {
case R.id.menu_history:
// Use explicit CallLogActivity intent instead of ACTION_VIEW +
@@ -1182,7 +1209,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
}
private boolean phoneIsInUse() {
- return getTelecomManager().isInCall();
+ return TelecomUtil.isInCall(this);
}
private boolean canIntentBeHandled(Intent intent) {
diff --git a/src/com/android/dialer/calllog/CallLogActivity.java b/src/com/android/dialer/calllog/CallLogActivity.java
index ff0726df7..ad795f9bd 100644
--- a/src/com/android/dialer/calllog/CallLogActivity.java
+++ b/src/com/android/dialer/calllog/CallLogActivity.java
@@ -48,6 +48,8 @@ public class CallLogActivity extends AppCompatActivity implements ViewPager.OnPa
private CallLogFragment mAllCallsFragment;
private CallLogFragment mMissedCallsFragment;
+ private boolean mIsVisible;
+
private String[] mTabTitles;
private static final int TAB_INDEX_ALL = 0;
@@ -161,6 +163,31 @@ public class CallLogActivity extends AppCompatActivity implements ViewPager.OnPa
}
@Override
+ protected void onStart() {
+ super.onStart();
+ mIsVisible = false;
+ }
+
+ @Override
+ protected void onStop() {
+ mIsVisible = false;
+ super.onStop();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ mIsVisible = false;
+ super.onSaveInstanceState(outState);
+ }
+
+ /**
+ * Returns true when the Activity is currently visible (between onStart and onStop).
+ */
+ /* package */ boolean isVisible() {
+ return mIsVisible;
+ }
+
+ @Override
public boolean onCreateOptionsMenu(Menu menu) {
final MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.call_log_options, menu);
@@ -180,6 +207,10 @@ public class CallLogActivity extends AppCompatActivity implements ViewPager.OnPa
@Override
public boolean onOptionsItemSelected(MenuItem item) {
+ if (!isVisible()) {
+ return true;
+ }
+
switch (item.getItemId()) {
case android.R.id.home:
final Intent intent = new Intent(this, DialtactsActivity.class);
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index 26e39651b..b7f068e1f 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -362,6 +362,10 @@ public class CallLogFragment extends Fragment implements CallLogQueryHandler.Lis
mAdapter.startCache();
rescheduleDisplayUpdate();
+
+ if (mVoicemailPlaybackPresenter != null) {
+ mVoicemailPlaybackPresenter.onResume();
+ }
}
@Override
diff --git a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
index 521b2a429..8b2eb6081 100644
--- a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
+++ b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
@@ -291,25 +291,22 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
.setOnMenuItemClickListener(this);
}
- try {
- mFilteredNumberAsyncQueryHandler.isBlocked(
- new FilteredNumberAsyncQueryHandler.OnCheckBlockedListener() {
- @Override
- public void onCheckComplete(Integer id) {
- blockId = id;
- int blockTitleId = blockId == null ? R.string.call_log_block_number
- : R.string.call_log_unblock_number;
- final MenuItem blockItem = menu.add(
- ContextMenu.NONE,
- R.id.context_menu_block_number,
- ContextMenu.NONE,
- blockTitleId);
- blockItem.setOnMenuItemClickListener(
- CallLogListItemViewHolder.this);
- }
- }, info.normalizedNumber, number, countryIso);
- } catch (IllegalArgumentException e) {
- }
+ mFilteredNumberAsyncQueryHandler.startBlockedQuery(
+ new FilteredNumberAsyncQueryHandler.OnCheckBlockedListener() {
+ @Override
+ public void onCheckComplete(Integer id) {
+ blockId = id;
+ int blockTitleId = blockId == null ? R.string.call_log_block_number
+ : R.string.call_log_unblock_number;
+ final MenuItem blockItem = menu.add(
+ ContextMenu.NONE,
+ R.id.context_menu_block_number,
+ ContextMenu.NONE,
+ blockTitleId);
+ blockItem.setOnMenuItemClickListener(
+ CallLogListItemViewHolder.this);
+ }
+ }, info.normalizedNumber, number, countryIso);
}
@Override
@@ -320,6 +317,8 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
FilterNumberDialogFragment.newInstance(blockId, info.normalizedNumber,
number, countryIso, info.formattedNumber);
newFragment.setQueryHandler(mFilteredNumberAsyncQueryHandler);
+ newFragment.setParentView(
+ ((Activity) mContext).findViewById(R.id.floating_action_button_container));
newFragment.show(((Activity) mContext).getFragmentManager(),
FilterNumberDialogFragment.BLOCK_DIALOG_FRAGMENT);
return true;
diff --git a/src/com/android/dialer/database/DialerDatabaseHelper.java b/src/com/android/dialer/database/DialerDatabaseHelper.java
index 413e8673f..cc8631d99 100644
--- a/src/com/android/dialer/database/DialerDatabaseHelper.java
+++ b/src/com/android/dialer/database/DialerDatabaseHelper.java
@@ -75,7 +75,7 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
* 0-98 KitKat
* </pre>
*/
- public static final int DATABASE_VERSION = 6;
+ public static final int DATABASE_VERSION = 7;
public static final String DATABASE_NAME = "dialer.db";
/**
@@ -457,7 +457,7 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
return;
}
- if (oldVersion < 6) {
+ if (oldVersion < 7) {
db.execSQL("DROP TABLE IF EXISTS " + Tables.FILTERED_NUMBER_TABLE);
db.execSQL("CREATE TABLE " + Tables.FILTERED_NUMBER_TABLE + " ("
+ FilteredNumberColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
@@ -470,7 +470,7 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
+ FilteredNumberColumns.TYPE + " INTEGER,"
+ FilteredNumberColumns.SOURCE + " INTEGER"
+ ");");
- oldVersion = 6;
+ oldVersion = 7;
}
if (oldVersion != DATABASE_VERSION) {
diff --git a/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java b/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
index 2fdea0d13..9da9cc15b 100644
--- a/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
+++ b/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
@@ -26,6 +26,7 @@ import android.database.sqlite.SQLiteDatabaseCorruptException;
import android.net.Uri;
import android.telephony.PhoneNumberUtils;
+import com.android.contacts.common.util.PhoneNumberHelper;
import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
import com.android.dialer.database.FilteredNumberContract.FilteredNumberSources;
@@ -79,22 +80,30 @@ public class FilteredNumberAsyncQueryHandler extends AsyncQueryHandler {
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- ((Listener) cookie).onQueryComplete(token, cookie, cursor);
+ if (cookie != null) {
+ ((Listener) cookie).onQueryComplete(token, cookie, cursor);
+ }
}
@Override
protected void onInsertComplete(int token, Object cookie, Uri uri) {
- ((Listener) cookie).onInsertComplete(token, cookie, uri);
+ if (cookie != null) {
+ ((Listener) cookie).onInsertComplete(token, cookie, uri);
+ }
}
@Override
protected void onUpdateComplete(int token, Object cookie, int result) {
- ((Listener) cookie).onUpdateComplete(token, cookie, result);
+ if (cookie != null) {
+ ((Listener) cookie).onUpdateComplete(token, cookie, result);
+ }
}
@Override
protected void onDeleteComplete(int token, Object cookie, int result) {
- ((Listener) cookie).onDeleteComplete(token, cookie, result);
+ if (cookie != null) {
+ ((Listener) cookie).onDeleteComplete(token, cookie, result);
+ }
}
private static Uri getContentUri(Integer id) {
@@ -105,26 +114,43 @@ public class FilteredNumberAsyncQueryHandler extends AsyncQueryHandler {
return uri;
}
+ public final void incrementFilteredCount(Integer id) {
+ startUpdate(NO_TOKEN, null,
+ ContentUris.withAppendedId(FilteredNumber.CONTENT_URI_INCREMENT_FILTERED_COUNT, id),
+ null, null, null);
+ }
+
/**
* Check if the number + country iso given has been blocked.
* This method normalizes the number for the lookup if normalizedNumber is null.
+ * @return {@code true} if the number was invalid and couldn't be checked,
+ * {@code false} otherwise,
*/
- public final void isBlocked(final OnCheckBlockedListener listener,
- String normalizedNumber, String number, String countryIso) {
+ public final boolean startBlockedQuery(final OnCheckBlockedListener listener,
+ String normalizedNumber, String number, String countryIso) {
if (normalizedNumber == null) {
- normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
+ normalizedNumber = getNormalizedNumber(number, countryIso);
if (normalizedNumber == null) {
- throw new IllegalArgumentException("Invalid phone number");
+ return true;
}
}
- isBlocked(listener, normalizedNumber);
+ startBlockedQuery(listener, normalizedNumber);
+ return false;
+ }
+
+ public static String getNormalizedNumber(String number, String countryIso) {
+ if (PhoneNumberHelper.isUriNumber(number)) {
+ return number;
+ } else {
+ return PhoneNumberUtils.formatNumberToE164(number, countryIso);
+ }
}
/**
* Check if the normalized number given has been blocked.
*/
- public final void isBlocked(final OnCheckBlockedListener listener,
- String normalizedNumber) {
+ public final void startBlockedQuery(final OnCheckBlockedListener listener,
+ String normalizedNumber) {
startQuery(NO_TOKEN,
new Listener() {
@Override
@@ -156,7 +182,7 @@ public class FilteredNumberAsyncQueryHandler extends AsyncQueryHandler {
public final void blockNumber(final OnBlockNumberListener listener,
String normalizedNumber, String number, String countryIso) {
if (normalizedNumber == null) {
- normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
+ normalizedNumber = getNormalizedNumber(number, countryIso);
}
ContentValues v = new ContentValues();
v.put(FilteredNumberColumns.NORMALIZED_NUMBER, normalizedNumber);
@@ -183,8 +209,8 @@ public class FilteredNumberAsyncQueryHandler extends AsyncQueryHandler {
/**
* Removes row from database.
- * Caller should call {@link FilteredNumberAsyncQueryHandler#isBlocked} first.
- * @param id The ID of row to remove, from {@link FilteredNumberAsyncQueryHandler#isBlocked}.
+ * Caller should call {@link FilteredNumberAsyncQueryHandler#startBlockedQuery} first.
+ * @param id The ID of row to remove, from {@link FilteredNumberAsyncQueryHandler#startBlockedQuery}.
*/
public final void unblock(final OnUnblockNumberListener listener, Integer id) {
if (id == null) {
diff --git a/src/com/android/dialer/database/FilteredNumberContract.java b/src/com/android/dialer/database/FilteredNumberContract.java
index 0ec171b15..f3966816c 100644
--- a/src/com/android/dialer/database/FilteredNumberContract.java
+++ b/src/com/android/dialer/database/FilteredNumberContract.java
@@ -16,13 +16,8 @@
package com.android.dialer.database;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.database.Cursor;
import android.net.Uri;
import android.provider.BaseColumns;
-import android.telephony.PhoneNumberUtils;
import com.android.dialerbind.ObjectFactory;
@@ -61,7 +56,7 @@ public final class FilteredNumberContract {
public interface FilteredNumberColumns {
// TYPE: INTEGER
- static final String _ID = "id";
+ static final String _ID = "_id";
/**
* Represents the number to be filtered, normalized to compare phone numbers for equality.
*
@@ -137,11 +132,17 @@ public final class FilteredNumberContract {
public static class FilteredNumber implements BaseColumns {
public static final String FILTERED_NUMBERS_TABLE = "filtered_numbers_table";
+ public static final String FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT =
+ "filtered_numbers_increment_filtered_count";
public static final Uri CONTENT_URI = Uri.withAppendedPath(
AUTHORITY_URI,
FILTERED_NUMBERS_TABLE);
+ public static final Uri CONTENT_URI_INCREMENT_FILTERED_COUNT = Uri.withAppendedPath(
+ AUTHORITY_URI,
+ FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT);
+
/**
* This utility class cannot be instantiated.
*/
diff --git a/src/com/android/dialer/database/FilteredNumberProvider.java b/src/com/android/dialer/database/FilteredNumberProvider.java
index 2bea7cae1..acb5e7f67 100644
--- a/src/com/android/dialer/database/FilteredNumberProvider.java
+++ b/src/com/android/dialer/database/FilteredNumberProvider.java
@@ -22,12 +22,12 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
+import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.os.Binder;
import android.text.TextUtils;
-import android.text.format.Time;
import android.util.Log;
import com.android.contacts.common.GeoUtil;
@@ -49,6 +49,7 @@ public class FilteredNumberProvider extends ContentProvider {
private static final int FILTERED_NUMBERS_TABLE = 1;
private static final int FILTERED_NUMBERS_TABLE_ID = 2;
+ private static final int FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT = 3;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
@@ -64,6 +65,10 @@ public class FilteredNumberProvider extends ContentProvider {
sUriMatcher.addURI(ObjectFactory.getFilteredNumberProviderAuthority(),
FilteredNumberContract.FilteredNumber.FILTERED_NUMBERS_TABLE + "/#",
FILTERED_NUMBERS_TABLE_ID);
+ sUriMatcher.addURI(ObjectFactory.getFilteredNumberProviderAuthority(),
+ FilteredNumberContract.FilteredNumber.FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT
+ + "/#",
+ FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT);
return true;
}
@@ -121,9 +126,7 @@ public class FilteredNumberProvider extends ContentProvider {
@VisibleForTesting
protected long getCurrentTimeMs() {
- Time timeNow = new Time();
- timeNow.setToNow();
- return timeNow.toMillis(false);
+ return System.currentTimeMillis();
}
private void setDefaultValues(ContentValues values) {
@@ -171,6 +174,20 @@ public class FilteredNumberProvider extends ContentProvider {
case FILTERED_NUMBERS_TABLE_ID:
selection = getSelectionWithId(selection, ContentUris.parseId(uri));
break;
+ case FILTERED_NUMBERS_INCREMENT_FILTERED_COUNT:
+ final long id = ContentUris.parseId(uri);
+ try {
+ db.execSQL(" UPDATE " + DialerDatabaseHelper.Tables.FILTERED_NUMBER_TABLE
+ + " SET" + FilteredNumberColumns.TIMES_FILTERED + "="
+ + FilteredNumberColumns.TIMES_FILTERED + "+1,"
+ + FilteredNumberColumns.LAST_TIME_FILTERED + "="
+ + getCurrentTimeMs()
+ + " WHERE " + FilteredNumberColumns._ID + "=" + id);
+ } catch (SQLException e) {
+ Log.d(TAG, "Could not update blocked statistics for " + id);
+ return 0;
+ }
+ return 1;
default:
throw new IllegalArgumentException("Unknown uri: " + uri);
}
diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java
index d2628da5e..54e4b8946 100644
--- a/src/com/android/dialer/dialpad/DialpadFragment.java
+++ b/src/com/android/dialer/dialpad/DialpadFragment.java
@@ -81,6 +81,7 @@ import com.android.dialer.R;
import com.android.dialer.SpecialCharSequenceMgr;
import com.android.dialer.calllog.PhoneAccountUtils;
import com.android.dialer.util.DialerUtils;
+import com.android.dialer.util.TelecomUtil;
import com.android.dialer.util.IntentUtil.CallIntentBuilder;
import com.android.incallui.Call.LogState;
import com.android.phone.common.CallLogAsync;
@@ -1478,8 +1479,12 @@ public class DialpadFragment extends Fragment
* @return true if the phone is "in use", meaning that at least one line
* is active (ie. off hook or ringing or dialing, or on hold).
*/
- public boolean isPhoneInUse() {
- return getTelecomManager().isInCall();
+ private boolean isPhoneInUse() {
+ final Context context = getActivity();
+ if (context != null) {
+ return TelecomUtil.isInCall(context);
+ }
+ return false;
}
/**
diff --git a/src/com/android/dialer/filterednumber/BlockedNumberAdapter.java b/src/com/android/dialer/filterednumber/BlockedNumberAdapter.java
new file mode 100644
index 000000000..504b5205b
--- /dev/null
+++ b/src/com/android/dialer/filterednumber/BlockedNumberAdapter.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2015 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.dialer.filterednumber;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.provider.ContactsContract;
+import android.text.BidiFormatter;
+import android.text.TextDirectionHeuristics;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.QuickContactBadge;
+import android.widget.SimpleCursorAdapter;
+import android.widget.TextView;
+
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.util.UriUtils;
+import com.android.dialer.R;
+import com.android.dialer.calllog.ContactInfo;
+import com.android.dialer.calllog.ContactInfoHelper;
+import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
+import com.android.dialer.util.PhoneNumberUtil;
+
+public class BlockedNumberAdapter extends SimpleCursorAdapter {
+
+ private Context mContext;
+ private ContactInfoHelper mContactInfoHelper;
+ private Resources mResources;
+ private BidiFormatter mBidiFormatter = BidiFormatter.getInstance();
+ private ContactPhotoManager mContactPhotoManager;
+ private FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
+
+ public BlockedNumberAdapter(Context context,
+ FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler) {
+ super(context, R.layout.blocked_number_item, null, new String[]{}, new int[]{}, 0);
+ mContext = context;
+ mContactInfoHelper = new ContactInfoHelper(context, GeoUtil.getCurrentCountryIso(context));
+ mContactPhotoManager = ContactPhotoManager.getInstance(context);
+ mResources = context.getResources();
+ mFilteredNumberAsyncQueryHandler = filteredNumberAsyncQueryHandler;
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ super.bindView(view, context, cursor);
+ final TextView callerName = (TextView) view.findViewById(R.id.caller_name);
+ final TextView callerNumber = (TextView) view.findViewById(R.id.caller_number);
+ final View deleteNumber = view.findViewById(R.id.delete_button);
+ final QuickContactBadge quickContactBadge =
+ (QuickContactBadge) view.findViewById(R.id.quick_contact_photo);
+ quickContactBadge.setOverlay(null);
+ quickContactBadge.setPrioritizedMimeType(
+ ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
+
+ final Integer id = cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns._ID));
+ final String countryIso = cursor.getString(cursor.getColumnIndex(
+ FilteredNumberColumns.COUNTRY_ISO));
+ final String number = cursor.getString(cursor.getColumnIndex(FilteredNumberColumns.NUMBER));
+ final String normalizedNumber = cursor.getString(cursor.getColumnIndex(
+ FilteredNumberColumns.NORMALIZED_NUMBER));
+ final ContactInfo info = mContactInfoHelper.lookupNumber(number, countryIso);
+ final CharSequence locationOrType = getNumberTypeOrLocation(info);
+ final String displayNumber = getDisplayNumber(info);
+ final String displayNumberStr = mBidiFormatter.unicodeWrap(
+ displayNumber.toString(), TextDirectionHeuristics.LTR);
+
+ deleteNumber.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ FilterNumberDialogFragment newFragment =
+ FilterNumberDialogFragment.newInstance(id, normalizedNumber, number,
+ countryIso, displayNumber);
+ newFragment.setQueryHandler(mFilteredNumberAsyncQueryHandler);
+ newFragment.setParentView(view);
+ newFragment.show(((Activity) mContext).getFragmentManager(),
+ FilterNumberDialogFragment.BLOCK_DIALOG_FRAGMENT);
+ }
+ });
+
+ String nameForDefaultImage;
+ if (!TextUtils.isEmpty(info.name)) {
+ nameForDefaultImage = info.name;
+ callerName.setText(info.name);
+ callerNumber.setText(locationOrType + " " + displayNumberStr);
+ } else {
+ nameForDefaultImage = displayNumber;
+ callerName.setText(displayNumberStr);
+ if (!TextUtils.isEmpty(locationOrType)) {
+ callerNumber.setText(locationOrType);
+ callerNumber.setVisibility(View.VISIBLE);
+ } else {
+ callerNumber.setVisibility(View.GONE);
+ }
+ }
+ loadContactPhoto(info, nameForDefaultImage, quickContactBadge);
+ }
+
+ private void loadContactPhoto(ContactInfo info, String displayName, QuickContactBadge badge) {
+ final String lookupKey = info.lookupUri == null
+ ? null : UriUtils.getLookupKeyFromUri(info.lookupUri);
+ final int contactType = mContactInfoHelper.isBusiness(info.sourceType)
+ ? ContactPhotoManager.TYPE_BUSINESS : ContactPhotoManager.TYPE_DEFAULT;
+ final DefaultImageRequest request = new DefaultImageRequest(displayName, lookupKey,
+ contactType, true /* isCircular */);
+ badge.assignContactUri(info.lookupUri);
+ badge.setContentDescription(
+ mResources.getString(R.string.description_contact_details, displayName));
+ mContactPhotoManager.loadDirectoryPhoto(badge, info.photoUri,
+ false /* darkTheme */, true /* isCircular */, request);
+ }
+
+ private String getDisplayNumber(ContactInfo info) {
+ if (!TextUtils.isEmpty(info.formattedNumber)) {
+ return info.formattedNumber;
+ } else if (!TextUtils.isEmpty(info.number)) {
+ return info.number;
+ } else {
+ return "";
+ }
+ }
+
+ private CharSequence getNumberTypeOrLocation(ContactInfo info) {
+ if (!TextUtils.isEmpty(info.name)) {
+ return ContactsContract.CommonDataKinds.Phone.getTypeLabel(mResources, info.type,
+ info.label);
+ } else {
+ return PhoneNumberUtil.getGeoDescription(mContext, info.number);
+ }
+ }
+}
diff --git a/src/com/android/dialer/filterednumber/BlockedNumberFragment.java b/src/com/android/dialer/filterednumber/BlockedNumberFragment.java
new file mode 100644
index 000000000..69fba34d4
--- /dev/null
+++ b/src/com/android/dialer/filterednumber/BlockedNumberFragment.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2015 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.dialer.filterednumber;
+
+import android.app.AlertDialog;
+import android.app.ListFragment;
+import android.app.LoaderManager;
+import android.content.CursorLoader;
+import android.content.DialogInterface;
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.dialog.IndeterminateProgressDialog;
+import com.android.dialer.R;
+import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnCheckBlockedListener;
+import com.android.dialer.database.FilteredNumberContract;
+
+public class BlockedNumberFragment extends ListFragment implements
+ LoaderManager.LoaderCallbacks<Cursor>, View.OnClickListener {
+
+ private BlockedNumberAdapter mAdapter;
+ private FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ getListView().addHeaderView(inflater.inflate(R.layout.blocked_number_header, null));
+ mFilteredNumberAsyncQueryHandler =
+ new FilteredNumberAsyncQueryHandler(getActivity().getContentResolver());
+ if (mAdapter == null) {
+ mAdapter = new BlockedNumberAdapter(getContext(), mFilteredNumberAsyncQueryHandler);
+ }
+ setListAdapter(mAdapter);
+ final Button addNumberBtn = (Button) getActivity().findViewById(R.id.add_number_button);
+ addNumberBtn.setOnClickListener(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ setListAdapter(null);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getLoaderManager().initLoader(0, null, this);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.blocked_number_fragment, container, false);
+ return view;
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ final String[] projection = {
+ FilteredNumberContract.FilteredNumberColumns._ID,
+ FilteredNumberContract.FilteredNumberColumns.COUNTRY_ISO,
+ FilteredNumberContract.FilteredNumberColumns.NUMBER,
+ FilteredNumberContract.FilteredNumberColumns.NORMALIZED_NUMBER
+ };
+ final String selection = FilteredNumberContract.FilteredNumberColumns.TYPE
+ + "=" + FilteredNumberContract.FilteredNumberTypes.BLOCKED_NUMBER;
+ final CursorLoader cursorLoader = new CursorLoader(
+ getContext(), FilteredNumberContract.FilteredNumber.CONTENT_URI, projection,
+ selection, null, null);
+ return cursorLoader;
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ mAdapter.swapCursor(data);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ mAdapter.swapCursor(null);
+ }
+
+ @Override
+ public void onClick(final View v) {
+ final String countryIso = GeoUtil.getCurrentCountryIso(getContext());
+ final EditText numberField = new EditText(getContext());
+ final DialogInterface.OnClickListener okListener = new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ final String number = numberField.getText().toString();
+ final IndeterminateProgressDialog progressDialog =
+ IndeterminateProgressDialog.show(getFragmentManager(),
+ getString(R.string.checkingNumber, number), null, 1000);
+ final String normalizedNumber =
+ FilteredNumberAsyncQueryHandler.getNormalizedNumber(number, countryIso);
+ if (normalizedNumber == null) {
+ progressDialog.dismiss();
+ Toast.makeText(getContext(), getString(R.string.invalidNumber, number),
+ Toast.LENGTH_LONG).show();
+ } else {
+ final OnCheckBlockedListener onCheckListener = new OnCheckBlockedListener() {
+ @Override
+ public void onCheckComplete(Integer id) {
+ progressDialog.dismiss();
+ if (id == null) {
+ FilterNumberDialogFragment newFragment =
+ FilterNumberDialogFragment.newInstance(id, normalizedNumber,
+ number, countryIso, number);
+ newFragment.setQueryHandler(mFilteredNumberAsyncQueryHandler);
+ newFragment.setParentView(v);
+ newFragment.show(getActivity().getFragmentManager(),
+ FilterNumberDialogFragment.BLOCK_DIALOG_FRAGMENT);
+ } else {
+ Toast.makeText(getContext(),
+ getString(R.string.alreadyBlocked, number),
+ Toast.LENGTH_LONG).show();
+ }
+ }
+ };
+ mFilteredNumberAsyncQueryHandler.startBlockedQuery(
+ onCheckListener, normalizedNumber, number, countryIso);
+ }
+ }
+ };
+ new AlertDialog.Builder(getContext())
+ .setTitle(getString(R.string.blockNumber))
+ .setView(numberField)
+ .setPositiveButton(getString(R.string.blockNumberOk), okListener)
+ .setNegativeButton(android.R.string.cancel, null)
+ .show();
+ }
+} \ No newline at end of file
diff --git a/src/com/android/dialer/filterednumber/FilterNumberDialogFragment.java b/src/com/android/dialer/filterednumber/FilterNumberDialogFragment.java
index f94d0f842..e9a88c845 100644
--- a/src/com/android/dialer/filterednumber/FilterNumberDialogFragment.java
+++ b/src/com/android/dialer/filterednumber/FilterNumberDialogFragment.java
@@ -39,11 +39,16 @@ public class FilterNumberDialogFragment extends DialogFragment {
private static final String ARG_DISPLAY_NUMBER = "argDisplayNumber";
private FilteredNumberAsyncQueryHandler mHandler;
+ private View mParentView;
public void setQueryHandler (FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler) {
mHandler = filteredNumberAsyncQueryHandler;
}
+ public void setParentView(View view) {
+ mParentView = view;
+ }
+
public static FilterNumberDialogFragment newInstance(Integer blockId, String normalizedNumber,
String number, String countryIso, String displayNumber) {
final FilterNumberDialogFragment fragment = new FilterNumberDialogFragment();
@@ -91,7 +96,6 @@ public class FilterNumberDialogFragment extends DialogFragment {
}
public void blockNumber() {
- final View view = getActivity().findViewById(R.id.floating_action_button_container);
final String displayNumber = getArguments().getString(ARG_DISPLAY_NUMBER);
final String message = getString(R.string.snackbar_number_blocked, displayNumber);
final String undoMessage = getString(R.string.snackbar_number_unblocked, displayNumber);
@@ -99,7 +103,7 @@ public class FilterNumberDialogFragment extends DialogFragment {
new FilteredNumberAsyncQueryHandler.OnUnblockNumberListener() {
@Override
public void onUnblockComplete(int rows, ContentValues values) {
- Snackbar.make(view, undoMessage, Snackbar.LENGTH_LONG).show();
+ Snackbar.make(mParentView, undoMessage, Snackbar.LENGTH_LONG).show();
}
};
@@ -107,7 +111,7 @@ public class FilterNumberDialogFragment extends DialogFragment {
new FilteredNumberAsyncQueryHandler.OnBlockNumberListener() {
@Override
public void onBlockComplete(final Uri uri) {
- Snackbar.make(view, message, Snackbar.LENGTH_LONG)
+ Snackbar.make(mParentView, message, Snackbar.LENGTH_LONG)
.setAction(R.string.block_number_undo,
// Delete the newly created row on 'undo'.
new View.OnClickListener() {
@@ -123,7 +127,6 @@ public class FilterNumberDialogFragment extends DialogFragment {
}
public void unblockNumber() {
- final View view = getActivity().findViewById(R.id.floating_action_button_container);
final String displayNumber = getArguments().getString(ARG_DISPLAY_NUMBER);
final String message = getString(R.string.snackbar_number_unblocked, displayNumber);
final String undoMessage = getString(R.string.snackbar_number_blocked, displayNumber);
@@ -131,14 +134,14 @@ public class FilterNumberDialogFragment extends DialogFragment {
new FilteredNumberAsyncQueryHandler.OnBlockNumberListener() {
@Override
public void onBlockComplete(final Uri uri) {
- Snackbar.make(view, undoMessage, Snackbar.LENGTH_LONG).show();
+ Snackbar.make(mParentView, undoMessage, Snackbar.LENGTH_LONG).show();
}
};
mHandler.unblock(
new FilteredNumberAsyncQueryHandler.OnUnblockNumberListener() {
@Override
public void onUnblockComplete(int rows, final ContentValues values) {
- Snackbar.make(view, message, Snackbar.LENGTH_LONG)
+ Snackbar.make(mParentView, message, Snackbar.LENGTH_LONG)
.setAction(R.string.block_number_undo,
new View.OnClickListener() {
// Re-insert the row on 'undo', with a new ID.
diff --git a/src/com/android/dialer/list/RegularSearchFragment.java b/src/com/android/dialer/list/RegularSearchFragment.java
index 5c3117d89..ec771e8ef 100644
--- a/src/com/android/dialer/list/RegularSearchFragment.java
+++ b/src/com/android/dialer/list/RegularSearchFragment.java
@@ -15,12 +15,10 @@
*/
package com.android.dialer.list;
-import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.READ_CONTACTS;
import android.app.Activity;
import android.content.pm.PackageManager;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.ViewGroup;
diff --git a/src/com/android/dialer/settings/DialerSettingsActivity.java b/src/com/android/dialer/settings/DialerSettingsActivity.java
index 01a9fcf5f..2b7277224 100644
--- a/src/com/android/dialer/settings/DialerSettingsActivity.java
+++ b/src/com/android/dialer/settings/DialerSettingsActivity.java
@@ -15,24 +15,20 @@
*/
package com.android.dialer.settings;
-import android.app.AppOpsManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
-import android.os.Process;
import android.os.UserManager;
-import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
-import android.util.Log;
import android.view.MenuItem;
import android.widget.Toast;
-import com.android.contacts.common.util.PermissionsUtil;
import com.android.dialer.R;
+import com.android.dialer.filterednumber.BlockedNumberFragment;
import java.util.List;
@@ -90,6 +86,11 @@ public class DialerSettingsActivity extends AppCompatPreferenceActivity {
target.add(phoneAccountSettingsHeader);
}
+ Header blockedCallsHeader = new Header();
+ blockedCallsHeader.titleRes = R.string.blocked_calls_settings_label;
+ blockedCallsHeader.fragment = BlockedNumberFragment.class.getName();
+ target.add(blockedCallsHeader);
+
if (telephonyManager.isTtyModeSupported()
|| telephonyManager.isHearingAidCompatibilitySupported()) {
Header accessibilitySettingsHeader = new Header();
diff --git a/src/com/android/dialer/util/DialerUtils.java b/src/com/android/dialer/util/DialerUtils.java
index fbe14ba9e..8870f76e4 100644
--- a/src/com/android/dialer/util/DialerUtils.java
+++ b/src/com/android/dialer/util/DialerUtils.java
@@ -33,18 +33,12 @@ import android.text.TextDirectionHeuristics;
import android.text.TextUtils;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
-import android.widget.ImageView;
-import android.widget.TextView;
import android.widget.Toast;
import com.android.contacts.common.ContactsUtils;
import com.android.contacts.common.interactions.TouchPointManager;
import com.android.dialer.R;
-import com.android.dialer.widget.EmptyContentView;
-import com.android.incallui.CallCardFragment;
-import com.android.incallui.Log;
-import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
@@ -94,7 +88,14 @@ public class DialerUtils {
}
final TelecomManager tm =
(TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
- tm.placeCall(intent.getData(), intent.getExtras());
+ if (TelecomUtil.hasCallPhonePermission(context)) {
+ tm.placeCall(intent.getData(), intent.getExtras());
+ } else {
+ // TODO: Make calling activity show request permission dialog and handle
+ // callback results appropriately.
+ Toast.makeText(context, "Cannot place call without Phone permission",
+ Toast.LENGTH_SHORT);
+ }
} else {
context.startActivity(intent);
}
diff --git a/src/com/android/dialer/util/PhoneNumberUtil.java b/src/com/android/dialer/util/PhoneNumberUtil.java
index 84f58aa85..539d8b9ba 100644
--- a/src/com/android/dialer/util/PhoneNumberUtil.java
+++ b/src/com/android/dialer/util/PhoneNumberUtil.java
@@ -25,13 +25,19 @@ import android.util.Log;
import android.util.Pair;
import com.android.contacts.common.util.PhoneNumberHelper;
+import com.android.contacts.common.util.TelephonyManagerUtils;
import com.google.common.collect.Sets;
+import com.google.i18n.phonenumbers.NumberParseException;
+import com.google.i18n.phonenumbers.Phonenumber;
+import com.google.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
import java.util.HashMap;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
public class PhoneNumberUtil {
+ private static final String TAG = "PhoneNumberUtil";
private static final Set<String> LEGACY_UNKNOWN_NUMBERS = Sets.newHashSet("-1", "-2", "-3");
/** Returns true if it is possible to place a call to the given number. */
@@ -92,4 +98,41 @@ public class PhoneNumberUtil {
public static boolean isLegacyUnknownNumbers(CharSequence number) {
return number != null && LEGACY_UNKNOWN_NUMBERS.contains(number.toString());
}
+
+ /**
+ * @return a geographical description string for the specified number.
+ * @see com.android.i18n.phonenumbers.PhoneNumberOfflineGeocoder
+ */
+ public static String getGeoDescription(Context context, String number) {
+ Log.v(TAG, "getGeoDescription('" + number + "')...");
+
+ if (TextUtils.isEmpty(number)) {
+ return null;
+ }
+
+ com.google.i18n.phonenumbers.PhoneNumberUtil util =
+ com.google.i18n.phonenumbers.PhoneNumberUtil.getInstance();
+ PhoneNumberOfflineGeocoder geocoder = PhoneNumberOfflineGeocoder.getInstance();
+
+ Locale locale = context.getResources().getConfiguration().locale;
+ String countryIso = TelephonyManagerUtils.getCurrentCountryIso(context, locale);
+ Phonenumber.PhoneNumber pn = null;
+ try {
+ Log.v(TAG, "parsing '" + number
+ + "' for countryIso '" + countryIso + "'...");
+ pn = util.parse(number, countryIso);
+ Log.v(TAG, "- parsed number: " + pn);
+ } catch (NumberParseException e) {
+ Log.v(TAG, "getGeoDescription: NumberParseException for incoming number '" +
+ number + "'");
+ }
+
+ if (pn != null) {
+ String description = geocoder.getDescriptionForNumber(pn, locale);
+ Log.v(TAG, "- got description: '" + description + "'");
+ return description;
+ }
+
+ return null;
+ }
}
diff --git a/src/com/android/dialer/util/TelecomUtil.java b/src/com/android/dialer/util/TelecomUtil.java
index 1cd270c9b..43b9a72a3 100644
--- a/src/com/android/dialer/util/TelecomUtil.java
+++ b/src/com/android/dialer/util/TelecomUtil.java
@@ -26,6 +26,9 @@ import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
+
public class TelecomUtil {
private static final String TAG = "TelecomUtil";
private static boolean sWarningLogged = false;
@@ -78,6 +81,20 @@ public class TelecomUtil {
return false;
}
+ public static List<PhoneAccountHandle> getCallCapablePhoneAccounts(Context context) {
+ if (hasReadPhoneStatePermission(context)) {
+ return getTelecomManager(context).getCallCapablePhoneAccounts();
+ }
+ return new ArrayList<>();
+ }
+
+ public static boolean isInCall(Context context) {
+ if (hasReadPhoneStatePermission(context)) {
+ return getTelecomManager(context).isInCall();
+ }
+ return false;
+ }
+
public static Uri getCallLogUri(Context context) {
return hasReadWriteVoicemailPermissions(context) ? Calls.CONTENT_URI_WITH_VOICEMAIL
: Calls.CONTENT_URI;
@@ -94,6 +111,16 @@ public class TelecomUtil {
|| hasPermission(context, Manifest.permission.MODIFY_PHONE_STATE);
}
+ public static boolean hasReadPhoneStatePermission(Context context) {
+ return isDefaultDialer(context)
+ || hasPermission(context, Manifest.permission.READ_PHONE_STATE);
+ }
+
+ public static boolean hasCallPhonePermission(Context context) {
+ return isDefaultDialer(context)
+ || hasPermission(context, Manifest.permission.CALL_PHONE);
+ }
+
private static boolean hasPermission(Context context, String permission) {
return context.checkSelfPermission(permission)
== PackageManager.PERMISSION_GRANTED;
diff --git a/src/com/android/dialer/voicemail/VoicemailAudioManager.java b/src/com/android/dialer/voicemail/VoicemailAudioManager.java
index e64e180b6..267eeca09 100644
--- a/src/com/android/dialer/voicemail/VoicemailAudioManager.java
+++ b/src/com/android/dialer/voicemail/VoicemailAudioManager.java
@@ -19,25 +19,36 @@ package com.android.dialer.voicemail;
import android.content.Context;
import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
+import android.telecom.CallAudioState;
import android.util.Log;
+import java.util.Objects;
import java.util.concurrent.RejectedExecutionException;
/**
* This class manages all audio changes for voicemail playback.
*/
-final class VoicemailAudioManager implements OnAudioFocusChangeListener {
+final class VoicemailAudioManager implements OnAudioFocusChangeListener,
+ WiredHeadsetManager.Listener {
private static final String TAG = VoicemailAudioManager.class.getSimpleName();
public static final int PLAYBACK_STREAM = AudioManager.STREAM_VOICE_CALL;
private AudioManager mAudioManager;
private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
+ private WiredHeadsetManager mWiredHeadsetManager;
+ private boolean mWasSpeakerOn;
+ private CallAudioState mCallAudioState;
public VoicemailAudioManager(Context context,
VoicemailPlaybackPresenter voicemailPlaybackPresenter) {
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
+ mWiredHeadsetManager = new WiredHeadsetManager(context);
+ mWiredHeadsetManager.setListener(this);
+
+ mCallAudioState = getInitialAudioState();
+ Log.i(TAG, "Initial audioState = " + mCallAudioState);
}
public void requestAudioFocus() {
@@ -60,14 +71,131 @@ final class VoicemailAudioManager implements OnAudioFocusChangeListener {
mVoicemailPlaybackPresenter.onAudioFocusChange(focusChange == AudioManager.AUDIOFOCUS_GAIN);
}
- public void turnOnSpeaker(boolean on) {
+ @Override
+ public void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn) {
+ Log.i(TAG, "wired headset was plugged in changed: " + oldIsPluggedIn
+ + " -> "+ newIsPluggedIn);
+
+ if (oldIsPluggedIn == newIsPluggedIn) {
+ return;
+ }
+
+ int newRoute = mCallAudioState.getRoute(); // start out with existing route
+ if (newIsPluggedIn) {
+ newRoute = CallAudioState.ROUTE_WIRED_HEADSET;
+ } else {
+ if (mWasSpeakerOn) {
+ newRoute = CallAudioState.ROUTE_SPEAKER;
+ } else {
+ newRoute = CallAudioState.ROUTE_EARPIECE;
+ }
+ }
+
+ mVoicemailPlaybackPresenter.setSpeakerphoneOn(newRoute == CallAudioState.ROUTE_SPEAKER);
+
+ // We need to call this every time even if we do not change the route because the supported
+ // routes changed either to include or not include WIRED_HEADSET.
+ setSystemAudioState(
+ new CallAudioState(false /* muted */, newRoute, calculateSupportedRoutes()));
+ }
+
+ public void setSpeakerphoneOn(boolean on) {
+ setAudioRoute(on ? CallAudioState.ROUTE_SPEAKER : CallAudioState.ROUTE_WIRED_OR_EARPIECE);
+ }
+
+ public boolean isWiredHeadsetPluggedIn() {
+ return mWiredHeadsetManager.isPluggedIn();
+ }
+
+ public void registerReceivers() {
+ // Receivers is plural because we expect to add bluetooth support.
+ mWiredHeadsetManager.registerReceiver();
+ }
+
+ public void unregisterReceivers() {
+ mWiredHeadsetManager.unregisterReceiver();
+ }
+
+ /**
+ * Change the audio route, for example from earpiece to speakerphone.
+ *
+ * @param route The new audio route to use. See {@link CallAudioState}.
+ */
+ void setAudioRoute(int route) {
+ Log.v(TAG, "setAudioRoute, route: " + CallAudioState.audioRouteToString(route));
+
+ // Change ROUTE_WIRED_OR_EARPIECE to a single entry.
+ int newRoute = selectWiredOrEarpiece(route, mCallAudioState.getSupportedRouteMask());
+
+ // If route is unsupported, do nothing.
+ if ((mCallAudioState.getSupportedRouteMask() | newRoute) == 0) {
+ Log.w(TAG, "Asking to set to a route that is unsupported: " + newRoute);
+ return;
+ }
+
+ if (mCallAudioState.getRoute() != newRoute) {
+ // Remember the new speaker state so it can be restored when the user plugs and unplugs
+ // a headset.
+ mWasSpeakerOn = newRoute == CallAudioState.ROUTE_SPEAKER;
+ setSystemAudioState(new CallAudioState(false /* muted */, newRoute,
+ mCallAudioState.getSupportedRouteMask()));
+ }
+ }
+
+ private CallAudioState getInitialAudioState() {
+ int supportedRouteMask = calculateSupportedRoutes();
+ int route = selectWiredOrEarpiece(CallAudioState.ROUTE_WIRED_OR_EARPIECE,
+ supportedRouteMask);
+ return new CallAudioState(false /* muted */, route, supportedRouteMask);
+ }
+
+ private int calculateSupportedRoutes() {
+ int routeMask = CallAudioState.ROUTE_SPEAKER;
+ if (mWiredHeadsetManager.isPluggedIn()) {
+ routeMask |= CallAudioState.ROUTE_WIRED_HEADSET;
+ } else {
+ routeMask |= CallAudioState.ROUTE_EARPIECE;
+ }
+ return routeMask;
+ }
+
+ private int selectWiredOrEarpiece(int route, int supportedRouteMask) {
+ // Since they are mutually exclusive and one is ALWAYS valid, we allow a special input of
+ // ROUTE_WIRED_OR_EARPIECE so that callers don't have to make a call to check which is
+ // supported before calling setAudioRoute.
+ if (route == CallAudioState.ROUTE_WIRED_OR_EARPIECE) {
+ route = CallAudioState.ROUTE_WIRED_OR_EARPIECE & supportedRouteMask;
+ if (route == 0) {
+ Log.wtf(TAG, "One of wired headset or earpiece should always be valid.");
+ // assume earpiece in this case.
+ route = CallAudioState.ROUTE_EARPIECE;
+ }
+ }
+ return route;
+ }
+
+ private void setSystemAudioState(CallAudioState callAudioState) {
+ CallAudioState oldAudioState = mCallAudioState;
+ mCallAudioState = callAudioState;
+
+ Log.i(TAG, "setSystemAudioState: changing from " + oldAudioState + " to "
+ + mCallAudioState);
+
+ // Audio route.
+ if (mCallAudioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
+ turnOnSpeaker(true);
+ } else if (mCallAudioState.getRoute() == CallAudioState.ROUTE_EARPIECE ||
+ mCallAudioState.getRoute() == CallAudioState.ROUTE_WIRED_HEADSET) {
+ // Just handle turning off the speaker, the system will handle switching between wired
+ // headset and earpiece.
+ turnOnSpeaker(false);
+ }
+ }
+
+ private void turnOnSpeaker(boolean on) {
if (mAudioManager.isSpeakerphoneOn() != on) {
Log.i(TAG, "turning speaker phone on: " + on);
mAudioManager.setSpeakerphoneOn(on);
}
}
-
- public boolean isSpeakerphoneOn() {
- return mAudioManager.isSpeakerphoneOn();
- }
} \ No newline at end of file
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
index 14c5473ae..f86fc5539 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
@@ -19,6 +19,7 @@ package com.android.dialer.voicemail;
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
@@ -238,6 +239,8 @@ public class VoicemailPlaybackLayout extends LinearLayout
private TextView mTotalDurationText;
private PositionUpdater mPositionUpdater;
+ private Drawable mVoicemailSeekHandleEnabled;
+ private Drawable mVoicemailSeekHandleDisabled;
public VoicemailPlaybackLayout(Context context) {
this(context, null);
@@ -276,9 +279,12 @@ public class VoicemailPlaybackLayout extends LinearLayout
mDeleteButton.setOnClickListener(mDeleteButtonListener);
mPositionText.setText(formatAsMinutesAndSeconds(0));
- mPositionText.setVisibility(View.INVISIBLE);
mTotalDurationText.setText(formatAsMinutesAndSeconds(0));
- mTotalDurationText.setVisibility(View.INVISIBLE);
+
+ mVoicemailSeekHandleEnabled = getResources().getDrawable(
+ R.drawable.ic_voicemail_seek_handle, mContext.getTheme());
+ mVoicemailSeekHandleDisabled = getResources().getDrawable(
+ R.drawable.ic_voicemail_seek_handle_disabled, mContext.getTheme());
}
@Override
@@ -317,6 +323,7 @@ public class VoicemailPlaybackLayout extends LinearLayout
mStateText.setText(getString(R.string.voicemail_playback_error));
}
+ @Override
public void onSpeakerphoneOn(boolean on) {
if (on) {
mPlaybackSpeakerphone.setImageResource(R.drawable.ic_volume_up_24dp);
@@ -354,7 +361,7 @@ public class VoicemailPlaybackLayout extends LinearLayout
@Override
public void setFetchContentTimeout() {
- disableUiElements();
+ mStartStopButton.setEnabled(true);
mStateText.setText(getString(R.string.voicemail_fetching_timout));
}
@@ -366,20 +373,22 @@ public class VoicemailPlaybackLayout extends LinearLayout
@Override
public void disableUiElements() {
mStartStopButton.setEnabled(false);
- mPlaybackSeek.setProgress(0);
mPlaybackSeek.setEnabled(false);
-
- mPositionText.setText(formatAsMinutesAndSeconds(0));
- mTotalDurationText.setText(formatAsMinutesAndSeconds(0));
+ mPlaybackSeek.setThumb(mVoicemailSeekHandleDisabled);
}
@Override
public void enableUiElements() {
mStartStopButton.setEnabled(true);
mPlaybackSeek.setEnabled(true);
+ mPlaybackSeek.setThumb(mVoicemailSeekHandleEnabled);
+ }
- mPositionText.setVisibility(View.VISIBLE);
- mTotalDurationText.setVisibility(View.VISIBLE);
+ @Override
+ public void resetSeekBar() {
+ mPlaybackSeek.setProgress(0);
+ mPlaybackSeek.setEnabled(false);
+ mPlaybackSeek.setThumb(mVoicemailSeekHandleDisabled);
}
@Override
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
index 95622bfa2..8b8b7c539 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
@@ -85,6 +85,7 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene
void setFetchContentTimeout();
void setIsFetchingContent();
void setPresenter(VoicemailPlaybackPresenter presenter, Uri voicemailUri);
+ void resetSeekBar();
}
public interface OnVoicemailDeletedListener {
@@ -101,6 +102,7 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene
private static final String[] HAS_CONTENT_PROJECTION = new String[] {
VoicemailContract.Voicemails.HAS_CONTENT,
+ VoicemailContract.Voicemails.DURATION
};
private static final int NUMBER_OF_THREADS_IN_POOL = 2;
@@ -251,19 +253,19 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene
mPosition = 0;
// Default to earpiece.
setSpeakerphoneOn(false);
+ mVoicemailAudioManager.setSpeakerphoneOn(false);
} else {
// Update the view to the current speakerphone state.
mView.onSpeakerphoneOn(mIsSpeakerphoneOn);
}
- mDuration.set(0);
+ checkForContent();
if (startPlayingImmediately) {
// Since setPlaybackView can get called during the view binding process, we don't
// want to reset mIsPlaying to false if the user is currently playing the
// voicemail and the view is rebound.
mIsPlaying = startPlayingImmediately;
- checkForContent();
}
}
}
@@ -272,16 +274,20 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene
* Reset the presenter for playback back to its original state.
*/
public void resetAll() {
- reset();
+ pausePresenter(true);
mView = null;
mVoicemailUri = null;
}
/**
- * Reset the presenter such that it is as if the voicemail has not been played.
+ * When navigating away from voicemail playback, we need to release the media player,
+ * pause the UI and save the position.
+ *
+ * @param reset {@code true} if we want to reset the position of the playback, {@code false} if
+ * we want to retain the current position (in case we return to the voicemail).
*/
- public void reset() {
+ public void pausePresenter(boolean reset) {
if (mMediaPlayer != null) {
mMediaPlayer.release();
mMediaPlayer = null;
@@ -291,19 +297,35 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene
mIsPrepared = false;
mIsPlaying = false;
- mPosition = 0;
- mDuration.set(0);
+
+ if (reset) {
+ // We want to reset the position whether or not the view is valid.
+ mPosition = 0;
+ }
if (mView != null) {
mView.onPlaybackStopped();
- mView.setClipPosition(0, mDuration.get());
+ if (reset) {
+ mView.setClipPosition(0, mDuration.get());
+ } else {
+ mPosition = mView.getDesiredClipPosition();
+ }
}
}
/**
+ * Must be invoked when the parent activity is resumed.
+ */
+ public void onResume() {
+ mVoicemailAudioManager.registerReceivers();
+ }
+
+ /**
* Must be invoked when the parent activity is paused.
*/
public void onPause() {
+ mVoicemailAudioManager.unregisterReceivers();
+
if (mContext != null && mIsPrepared
&& mInitialOrientation != mContext.getResources().getConfiguration().orientation) {
// If an orientation change triggers the pause, retain the MediaPlayer.
@@ -312,11 +334,12 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene
}
// Release the media player, otherwise there may be failures.
- reset();
+ pausePresenter(false);
if (mActivity != null) {
mActivity.getWindow().clearFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
}
+
}
/**
@@ -345,8 +368,8 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene
* voicemail we've been asked to play has any content available.
* <p>
* Notify the user that we are fetching the content, then check to see if the content field in
- * the DB is set. If set, we proceed to {@link #prepareContent()} method. If not set, make
- * a request to fetch the content asynchronously via {@link #requestContent()}.
+ * the DB is set. If set, we proceed to {@link #prepareContent()} method. We get the duration of
+ * the voicemail from the query and set it if the content is not available.
*/
private void checkForContent() {
mAsyncTaskExecutor.submit(Tasks.CHECK_FOR_CONTENT, new AsyncTask<Void, Void, Boolean>() {
@@ -360,7 +383,8 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene
if (hasContent) {
prepareContent();
} else {
- requestContent();
+ mView.resetSeekBar();
+ mView.setClipPosition(0, mDuration.get());
}
}
});
@@ -373,10 +397,14 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene
ContentResolver contentResolver = mContext.getContentResolver();
Cursor cursor = contentResolver.query(
- voicemailUri, HAS_CONTENT_PROJECTION, null, null, null);
+ voicemailUri, null, null, null, null);
try {
if (cursor != null && cursor.moveToNext()) {
- return cursor.getInt(cursor.getColumnIndexOrThrow(
+ int duration = cursor.getInt(cursor.getColumnIndex(
+ VoicemailContract.Voicemails.DURATION));
+ // Convert database duration (seconds) into mDuration (milliseconds)
+ mDuration.set(duration > 0 ? duration * 1000 : 0);
+ return cursor.getInt(cursor.getColumnIndex(
VoicemailContract.Voicemails.HAS_CONTENT)) == 1;
}
} finally {
@@ -519,7 +547,6 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene
mIsPrepared = true;
mDuration.set(mMediaPlayer.getDuration());
- mPosition = mMediaPlayer.getCurrentPosition();
Log.d(TAG, "onPrepared: mPosition=" + mPosition);
mView.setClipPosition(mPosition, mDuration.get());
@@ -604,7 +631,7 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene
if (!mIsPrepared) {
// If we haven't downloaded the voicemail yet, attempt to download it.
- checkForContent();
+ requestContent();
mIsPlaying = true;
return;
}
@@ -614,15 +641,15 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene
if (!mMediaPlayer.isPlaying()) {
// Clamp the start position between 0 and the duration.
mPosition = Math.max(0, Math.min(mPosition, mDuration.get()));
+
mMediaPlayer.seekTo(mPosition);
try {
// Grab audio focus.
// Can throw RejectedExecutionException.
mVoicemailAudioManager.requestAudioFocus();
-
- setSpeakerphoneOn(mIsSpeakerphoneOn);
mMediaPlayer.start();
+ setSpeakerphoneOn(mIsSpeakerphoneOn);
} catch (RejectedExecutionException e) {
handleError(e);
}
@@ -708,21 +735,29 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene
}
}
+ /**
+ * This is for use by UI interactions only. It simplifies UI logic.
+ */
public void toggleSpeakerphone() {
+ mVoicemailAudioManager.setSpeakerphoneOn(!mIsSpeakerphoneOn);
setSpeakerphoneOn(!mIsSpeakerphoneOn);
}
- private void setSpeakerphoneOn(boolean on) {
+ /**
+ * This method only handles app-level changes to the speakerphone. Audio layer changes should
+ * be handled separately. This is so that the VoicemailAudioManager can trigger changes to
+ * the presenter without the presenter triggering the audio manager and duplicating actions.
+ */
+ public void setSpeakerphoneOn(boolean on) {
mView.onSpeakerphoneOn(on);
- if (mIsSpeakerphoneOn == on) {
- return;
- }
mIsSpeakerphoneOn = on;
- mVoicemailAudioManager.turnOnSpeaker(on);
+ // This should run even if speakerphone is not being toggled because we may be switching
+ // from earpiece to headphone and vise versa. Also upon initial setup the default audio
+ // source is the earpiece, so we want to trigger the proximity sensor.
if (mIsPlaying) {
- if (on) {
+ if (on || mVoicemailAudioManager.isWiredHeadsetPluggedIn()) {
disableProximitySensor(false /* waitForFarState */);
if (mIsPrepared && mMediaPlayer != null && mMediaPlayer.isPlaying()) {
mActivity.getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
diff --git a/src/com/android/dialer/voicemail/WiredHeadsetManager.java b/src/com/android/dialer/voicemail/WiredHeadsetManager.java
new file mode 100644
index 000000000..7351f4f01
--- /dev/null
+++ b/src/com/android/dialer/voicemail/WiredHeadsetManager.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 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.dialer.voicemail;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.util.Log;
+
+/** Listens for and caches headset state. */
+class WiredHeadsetManager {
+ private static final String TAG = WiredHeadsetManager.class.getSimpleName();
+
+ interface Listener {
+ void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn);
+ }
+
+ /** Receiver for wired headset plugged and unplugged events. */
+ private class WiredHeadsetBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (AudioManager.ACTION_HEADSET_PLUG.equals(intent.getAction())) {
+ boolean isPluggedIn = intent.getIntExtra("state", 0) == 1;
+ Log.v(TAG, "ACTION_HEADSET_PLUG event, plugged in: " + isPluggedIn);
+ onHeadsetPluggedInChanged(isPluggedIn);
+ }
+ }
+ }
+
+ private final WiredHeadsetBroadcastReceiver mReceiver;
+ private boolean mIsPluggedIn;
+ private Listener mListener;
+ private Context mContext;
+
+ WiredHeadsetManager(Context context) {
+ mContext = context;
+ mReceiver = new WiredHeadsetBroadcastReceiver();
+
+ AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ mIsPluggedIn = audioManager.isWiredHeadsetOn();
+
+ }
+
+ void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ boolean isPluggedIn() {
+ return mIsPluggedIn;
+ }
+
+ void registerReceiver() {
+ // Register for misc other intent broadcasts.
+ IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
+ mContext.registerReceiver(mReceiver, intentFilter);
+ }
+
+ void unregisterReceiver() {
+ mContext.unregisterReceiver(mReceiver);
+ }
+
+ private void onHeadsetPluggedInChanged(boolean isPluggedIn) {
+ if (mIsPluggedIn != isPluggedIn) {
+ Log.v(TAG, "onHeadsetPluggedInChanged, mIsPluggedIn: " + mIsPluggedIn + " -> "
+ + isPluggedIn);
+ boolean oldIsPluggedIn = mIsPluggedIn;
+ mIsPluggedIn = isPluggedIn;
+ if (mListener != null) {
+ mListener.onWiredHeadsetPluggedInChanged(oldIsPluggedIn, mIsPluggedIn);
+ }
+ }
+ }
+} \ No newline at end of file