Merge "Balls test app."
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index c66b2de..556fb10 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -738,6 +738,27 @@
}
/**
+ * Get the current connection state of the local Bluetooth adapter.
+ * This can be used to check whether the local Bluetooth adapter is connected
+ * to any profile of any other remote Bluetooth Device.
+ *
+ * <p> Use this function along with {@link #ACTION_CONNECTION_STATE_CHANGED}
+ * intent to get the connection state of the adapter.
+ *
+ * @return One of {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTED},
+ * {@link #STATE_CONNECTING} or {@link #STATE_DISCONNECTED}
+ *
+ * @hide
+ */
+ public int getConnectionState() {
+ if (getState() != STATE_ON) return BluetoothAdapter.STATE_DISCONNECTED;
+ try {
+ return mService.getAdapterConnectionState();
+ } catch (RemoteException e) {Log.e(TAG, "getConnectionState:", e);}
+ return BluetoothAdapter.STATE_DISCONNECTED;
+ }
+
+ /**
* Picks RFCOMM channels until none are left.
* Avoids reserved channels.
*/
@@ -879,6 +900,7 @@
return socket;
}
+
/**
* Construct an unencrypted, unauthenticated, RFCOMM server socket.
* Call #accept to retrieve connections to this socket.
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 1eb269d..aefb3f2 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -47,6 +47,8 @@
boolean isDiscovering();
byte[] readOutOfBandData();
+ int getAdapterConnectionState();
+
boolean createBond(in String address);
boolean createBondOutOfBand(in String address, in byte[] hash, in byte[] randomizer);
boolean cancelBondProcess(in String address);
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 6089013..c0743cfe 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1296,7 +1296,7 @@
mInShowWindow = false;
}
}
-
+
void showWindowInner(boolean showInput) {
boolean doShowInput = false;
boolean wasVisible = mWindowVisible;
@@ -1311,7 +1311,7 @@
} else {
showInput = true;
}
-
+
if (DEBUG) Log.v(TAG, "showWindow: updating UI");
initialize();
updateFullscreenMode();
@@ -1343,14 +1343,15 @@
if (doShowInput) {
startExtractingText(false);
}
-
+
if (!wasVisible) {
if (DEBUG) Log.v(TAG, "showWindow: showing!");
+ mImm.setIMEButtonVisible(mToken, true);
onWindowShown();
mWindow.show();
}
}
-
+
public void hideWindow() {
if (mInputViewStarted) {
if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
@@ -1364,11 +1365,12 @@
if (mWindowVisible) {
mWindow.hide();
mWindowVisible = false;
+ mImm.setIMEButtonVisible(mToken, false);
onWindowHidden();
mWindowWasVisible = false;
}
}
-
+
/**
* Called when the input method window has been shown to the user, after
* previously not being visible. This is done after all of the UI setup
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 65047c0..7289012 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -167,6 +167,8 @@
private static String mDockAddress;
private String mDockPin;
+ private int mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
+
private static class RemoteService {
public String address;
public ParcelUuid uuid;
@@ -415,6 +417,7 @@
mProfilesConnected = 0;
mProfilesConnecting = 0;
mProfilesDisconnecting = 0;
+ mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
if (saveSetting) {
persistBluetoothOnSetting(false);
@@ -2737,11 +2740,18 @@
}
}
+ public int getAdapterConnectionState() {
+ return mAdapterConnectionState;
+ }
+
public synchronized void sendConnectionStateChange(BluetoothDevice device, int state,
int prevState) {
if (updateCountersAndCheckForConnectionStateChange(device, state, prevState)) {
- state = getAdapterConnectionState(state);
- prevState = getAdapterConnectionState(prevState);
+ state = translateToAdapterConnectionState(state);
+ prevState = translateToAdapterConnectionState(prevState);
+
+ mAdapterConnectionState = state;
+
Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, state);
@@ -2751,7 +2761,7 @@
}
}
- private int getAdapterConnectionState(int state) {
+ private int translateToAdapterConnectionState(int state) {
switch (state) {
case BluetoothProfile.STATE_CONNECTING:
return BluetoothAdapter.STATE_CONNECTING;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 7cb6291..033ee7c 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -519,6 +519,15 @@
}
/** @hide */
+ public void setIMEButtonVisible(IBinder imeToken, boolean visible) {
+ try {
+ mService.setIMEButtonVisible(imeToken, visible);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** @hide */
public void setFullscreenMode(boolean fullScreen) {
mFullscreenMode = fullScreen;
}
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index c8e3935..6f6b40b 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -18,11 +18,11 @@
import com.android.internal.R;
-import android.app.Activity;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.ListFragment;
import android.app.backup.BackupManager;
+import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Bundle;
@@ -45,61 +45,65 @@
public void onLocaleSelected(Locale locale);
}
- Loc[] mLocales;
- String[] mSpecialLocaleCodes;
- String[] mSpecialLocaleNames;
-
- private Locale mNewLocale;
-
LocaleSelectionListener mListener; // default to null
- private static class Loc implements Comparable<Loc> {
- static Collator sCollator = Collator.getInstance();
+ public static class LocaleInfo implements Comparable<LocaleInfo> {
+ static final Collator sCollator = Collator.getInstance();
String label;
Locale locale;
- public Loc(String label, Locale locale) {
+ public LocaleInfo(String label, Locale locale) {
this.label = label;
this.locale = locale;
}
+ public String getLabel() {
+ return label;
+ }
+
+ public Locale getLocale() {
+ return locale;
+ }
+
@Override
public String toString() {
return this.label;
}
@Override
- public int compareTo(Loc another) {
+ public int compareTo(LocaleInfo another) {
return sCollator.compare(this.label, another.label);
}
}
- private void setUpLocaleList() {
- final Activity activity = getActivity();
- final Resources resources = activity.getResources();
- mSpecialLocaleCodes = resources.getStringArray(R.array.special_locale_codes);
- mSpecialLocaleNames = resources.getStringArray(R.array.special_locale_names);
-
- final String[] locales = activity.getAssets().getLocales();
+ /**
+ * Constructs an Adapter object containing Locale information. Content is sorted by
+ * {@link LocaleInfo#label}.
+ */
+ public static ArrayAdapter<LocaleInfo> constructAdapter(Context context) {
+ final Resources resources = context.getResources();
+ final String[] locales = context.getAssets().getLocales();
+ final String[] specialLocaleCodes = resources.getStringArray(R.array.special_locale_codes);
+ final String[] specialLocaleNames = resources.getStringArray(R.array.special_locale_names);
Arrays.sort(locales);
final int origSize = locales.length;
- Loc[] preprocess = new Loc[origSize];
+ final LocaleInfo[] preprocess = new LocaleInfo[origSize];
int finalSize = 0;
for (int i = 0 ; i < origSize; i++ ) {
- String s = locales[i];
- int len = s.length();
+ final String s = locales[i];
+ final int len = s.length();
if (len == 5) {
String language = s.substring(0, 2);
String country = s.substring(3, 5);
- Locale l = new Locale(language, country);
+ final Locale l = new Locale(language, country);
if (finalSize == 0) {
if (DEBUG) {
Log.v(TAG, "adding initial "+ toTitleCase(l.getDisplayLanguage(l)));
}
preprocess[finalSize++] =
- new Loc(toTitleCase(l.getDisplayLanguage(l)), l);
+ new LocaleInfo(toTitleCase(l.getDisplayLanguage(l)), l);
} else {
// check previous entry:
// same lang and a country -> upgrade to full name and
@@ -110,15 +114,20 @@
if (DEBUG) {
Log.v(TAG, "backing up and fixing "+
preprocess[finalSize-1].label+" to "+
- getDisplayName(preprocess[finalSize-1].locale));
+ getDisplayName(preprocess[finalSize-1].locale,
+ specialLocaleCodes, specialLocaleNames));
}
preprocess[finalSize-1].label = toTitleCase(
- getDisplayName(preprocess[finalSize-1].locale));
+ getDisplayName(preprocess[finalSize-1].locale,
+ specialLocaleCodes, specialLocaleNames));
if (DEBUG) {
- Log.v(TAG, " and adding "+ toTitleCase(getDisplayName(l)));
+ Log.v(TAG, " and adding "+ toTitleCase(
+ getDisplayName(l, specialLocaleCodes, specialLocaleNames)));
}
preprocess[finalSize++] =
- new Loc(toTitleCase(getDisplayName(l)), l);
+ new LocaleInfo(toTitleCase(
+ getDisplayName(
+ l, specialLocaleCodes, specialLocaleNames)), l);
} else {
String displayName;
if (s.equals("zz_ZZ")) {
@@ -129,31 +138,20 @@
if (DEBUG) {
Log.v(TAG, "adding "+displayName);
}
- preprocess[finalSize++] = new Loc(displayName, l);
+ preprocess[finalSize++] = new LocaleInfo(displayName, l);
}
}
}
}
- mLocales = new Loc[finalSize];
- for (int i = 0; i < finalSize ; i++) {
- mLocales[i] = preprocess[i];
+
+ final LocaleInfo[] localeInfos = new LocaleInfo[finalSize];
+ for (int i = 0; i < finalSize; i++) {
+ localeInfos[i] = preprocess[i];
}
- Arrays.sort(mLocales);
+ Arrays.sort(localeInfos);
final int layoutId = R.layout.locale_picker_item;
final int fieldId = R.id.locale;
- final ArrayAdapter<Loc> adapter =
- new ArrayAdapter<Loc>(activity, layoutId, fieldId, mLocales);
- setListAdapter(adapter);
- }
-
- @Override
- public void onActivityCreated(final Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- setUpLocaleList();
- }
-
- public void setLocaleSelectionListener(LocaleSelectionListener listener) {
- mListener = listener;
+ return new ArrayAdapter<LocaleInfo>(context, layoutId, fieldId, localeInfos);
}
private static String toTitleCase(String s) {
@@ -164,12 +162,13 @@
return Character.toUpperCase(s.charAt(0)) + s.substring(1);
}
- private String getDisplayName(Locale l) {
+ private static String getDisplayName(
+ Locale l, String[] specialLocaleCodes, String[] specialLocaleNames) {
String code = l.toString();
- for (int i = 0; i < mSpecialLocaleCodes.length; i++) {
- if (mSpecialLocaleCodes[i].equals(code)) {
- return mSpecialLocaleNames[i];
+ for (int i = 0; i < specialLocaleCodes.length; i++) {
+ if (specialLocaleCodes[i].equals(code)) {
+ return specialLocaleNames[i];
}
}
@@ -177,6 +176,17 @@
}
@Override
+ public void onActivityCreated(final Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ final ArrayAdapter<LocaleInfo> adapter = constructAdapter(getActivity());
+ setListAdapter(adapter);
+ }
+
+ public void setLocaleSelectionListener(LocaleSelectionListener listener) {
+ mListener = listener;
+ }
+
+ @Override
public void onResume() {
super.onResume();
getListView().requestFocus();
@@ -191,7 +201,8 @@
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
if (mListener != null) {
- mListener.onLocaleSelected(mLocales[position].locale);
+ final Locale locale = ((LocaleInfo)getListAdapter().getItem(position)).locale;
+ mListener.onLocaleSelected(locale);
}
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 34a5b11..f87ca3e 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -32,5 +32,6 @@
void animateCollapse();
void setLightsOn(boolean on);
void setMenuKeyVisible(boolean visible);
+ void setIMEButtonVisible(boolean visible);
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 90f4d48..d5ae1f0 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -32,6 +32,7 @@
void removeIcon(String slot);
void setActiveWindowIsFullscreen(boolean fullscreen);
void setMenuKeyVisible(boolean visible);
+ void setIMEButtonVisible(boolean visible);
// ---- Methods below are for use by the status bar policy services ----
// You need the STATUS_BAR_SERVICE permission
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 49ae2bc7..ca1cd59 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -55,6 +55,7 @@
void hideMySoftInput(in IBinder token, int flags);
void showMySoftInput(in IBinder token, int flags);
void updateStatusIcon(in IBinder token, String packageName, int iconId);
+ void setIMEButtonVisible(in IBinder token, boolean visible);
InputMethodSubtype getCurrentInputMethodSubtype();
boolean setInputMethodEnabled(String id, boolean enabled);
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notification_peek.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notification_peek.xml
new file mode 100644
index 0000000..a7c91f5
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notification_peek.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2006, 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.
+*/
+-->
+
+<!-- android:background="@drawable/status_bar_closed_default_background" -->
+<com.android.systemui.statusbar.tablet.NotificationPanel
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:background="@*android:drawable/dialog_full_holo_dark"
+ android:orientation="vertical"
+ >
+
+ <FrameLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal|bottom"
+ android:animationCache="false"
+ android:orientation="vertical"
+ android:background="@drawable/status_bar_background"
+ android:clickable="true"
+ android:focusable="true"
+ android:descendantFocusability="afterDescendants"
+ >
+ </FrameLayout>
+</com.android.systemui.statusbar.tablet.NotificationPanel>
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
index 80c9a49..8cbf3e3 100644
--- a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
@@ -23,20 +23,23 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:background="#FF000000"
+ android:background="@*android:drawable/dialog_full_holo_dark"
android:orientation="vertical"
+ android:animateLayoutChanges="true"
>
<ScrollView
android:id="@+id/notificationScroller"
android:layout_height="wrap_content"
android:layout_width="match_parent"
+ android:animateLayoutChanges="true"
>
<LinearLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal|bottom"
+ android:animateLayoutChanges="true"
android:animationCache="false"
android:orientation="vertical"
android:background="@drawable/status_bar_background"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index c164eb4..ed2ed1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -55,6 +55,7 @@
private static final int MSG_SET_LIGHTS_ON = 0x00070000;
private static final int MSG_SHOW_MENU = 0x00080000;
+ private static final int MSG_SHOW_IME_BUTTON = 0x00090000;
private StatusBarIconList mList;
private Callbacks mCallbacks;
@@ -81,6 +82,7 @@
public void animateCollapse();
public void setLightsOn(boolean on);
public void setMenuKeyVisible(boolean visible);
+ public void setIMEButtonVisible(boolean visible);
}
public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
@@ -163,6 +165,13 @@
}
}
+ public void setIMEButtonVisible(boolean visible) {
+ synchronized (mList) {
+ mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
+ mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, visible ? 1 : 0, 0, null).sendToTarget();
+ }
+ }
+
private final class H extends Handler {
public void handleMessage(Message msg) {
final int what = msg.what & MSG_MASK;
@@ -223,6 +232,9 @@
case MSG_SHOW_MENU:
mCallbacks.setMenuKeyVisible(msg.arg1 != 0);
break;
+ case MSG_SHOW_IME_BUTTON:
+ mCallbacks.setIMEButtonVisible(msg.arg1 != 0);
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
index bf58b37..b174973 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
@@ -1014,6 +1014,7 @@
// Not supported
public void setMenuKeyVisible(boolean visible) { }
+ public void setIMEButtonVisible(boolean visible) { }
private class Launcher implements View.OnClickListener {
private PendingIntent mIntent;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
index ae1fdbd..256386b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
@@ -65,7 +65,7 @@
mCommandQueue = new CommandQueue(this, iconList);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- boolean[] switches = new boolean[2];
+ boolean[] switches = new boolean[3];
try {
mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
switches);
@@ -75,6 +75,7 @@
setLightsOn(switches[0]);
setMenuKeyVisible(switches[1]);
+ setIMEButtonVisible(switches[2]);
// Set up the initial icon state
int N = iconList.size();
@@ -119,6 +120,7 @@
+ " icons=" + iconList.size()
+ " lights=" + (switches[0]?"on":"off")
+ " menu=" + (switches[1]?"visible":"invisible")
+ + " imeButton=" + (switches[2]?"visible":"invisible")
);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java
index 56b4f24..c52bd4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java
@@ -16,7 +16,10 @@
package com.android.systemui.statusbar.tablet;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.provider.Settings;
@@ -32,7 +35,9 @@
import com.android.server.InputMethodManagerService;
import com.android.systemui.R;
+import java.util.Calendar;
import java.util.List;
+import java.util.TimeZone;
public class InputMethodButton extends ImageView {
@@ -61,8 +66,10 @@
});
}
+ @Override
protected void onAttachedToWindow() {
mIcon = (ImageView) findViewById(R.id.imeButton);
+
refreshStatusIcon(mKeyboardShown);
}
@@ -127,13 +134,8 @@
});
}
- public void showSoftInput() {
- mKeyboardShown = true;
- postRefreshStatusIcon();
- }
-
- public void hideSoftInput() {
- mKeyboardShown = false;
+ public void setIMEButtonVisible(boolean visible) {
+ mKeyboardShown = visible;
postRefreshStatusIcon();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java
index 2b4f9d2..df29f95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java
@@ -24,6 +24,7 @@
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ImageView;
+import android.view.MotionEvent;
import com.android.systemui.R;
@@ -43,12 +44,12 @@
public IconLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
- }
- static class DraggerView extends View {
- public DraggerView(Context context, AttributeSet attrs) {
- super(context, attrs);
+ public boolean onInterceptTouchEvent(MotionEvent e) {
+ return true;
}
}
}
+
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
index a01220f..9dc7850 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
@@ -18,7 +18,10 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import android.animation.LayoutTransition;
+import android.animation.ObjectAnimator;
import android.app.ActivityManagerNative;
import android.app.PendingIntent;
import android.app.Notification;
@@ -41,6 +44,7 @@
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
@@ -65,6 +69,8 @@
public static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
public static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
+ public static final int MSG_OPEN_NOTIFICATION_PEEK = 1002;
+ public static final int MSG_CLOSE_NOTIFICATION_PEEK = 1003;
public static final int MSG_OPEN_SYSTEM_PANEL = 1010;
public static final int MSG_CLOSE_SYSTEM_PANEL = 1011;
public static final int MSG_OPEN_RECENTS_PANEL = 1020;
@@ -89,8 +95,17 @@
View mMenuButton;
View mRecentButton;
+ InputMethodButton mInputMethodButton;
+
NotificationPanel mNotificationPanel;
SystemPanel mSystemPanel;
+ NotificationPanel mNotificationPeekWindow;
+ ViewGroup mNotificationPeekRow;
+ int mNotificationPeekIndex;
+ LayoutTransition mNotificationPeekScrubLeft, mNotificationPeekScrubRight;
+
+ int mNotificationPeekTapDuration;
+ int mNotificationFlingVelocity;
ViewGroup mPile;
TextView mClearButton;
@@ -132,7 +147,7 @@
mStatusBarView.setIgnoreChildren(0, mNotificationTrigger, mNotificationPanel);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- 400, // ViewGroup.LayoutParams.WRAP_CONTENT,
+ 512, // ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
@@ -144,6 +159,41 @@
WindowManagerImpl.getDefault().addView(mNotificationPanel, lp);
+ // Notification preview window
+ mNotificationPeekWindow = (NotificationPanel) View.inflate(context,
+ R.layout.sysbar_panel_notification_peek, null);
+ mNotificationPeekRow = (ViewGroup) mNotificationPeekWindow.findViewById(R.id.content);
+ mNotificationPeekWindow.setVisibility(View.GONE);
+ mNotificationPeekWindow.setOnTouchListener(
+ new TouchOutsideListener(MSG_CLOSE_NOTIFICATION_PANEL, mNotificationPeekWindow));
+ mNotificationPeekScrubRight = new LayoutTransition();
+ mNotificationPeekScrubRight.setAnimator(LayoutTransition.APPEARING,
+ ObjectAnimator.ofInt(null, "left", -512, 0));
+ mNotificationPeekScrubRight.setAnimator(LayoutTransition.DISAPPEARING,
+ ObjectAnimator.ofInt(null, "left", -512, 0));
+ mNotificationPeekScrubRight.setDuration(500);
+
+ mNotificationPeekScrubLeft = new LayoutTransition();
+ mNotificationPeekScrubLeft.setAnimator(LayoutTransition.APPEARING,
+ ObjectAnimator.ofInt(null, "left", 512, 0));
+ mNotificationPeekScrubLeft.setAnimator(LayoutTransition.DISAPPEARING,
+ ObjectAnimator.ofInt(null, "left", 512, 0));
+ mNotificationPeekScrubLeft.setDuration(500);
+
+ // XXX: setIgnoreChildren?
+ lp = new WindowManager.LayoutParams(
+ 512, // ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+ PixelFormat.TRANSLUCENT);
+ lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+ lp.setTitle("NotificationPeekWindow");
+ lp.windowAnimations = com.android.internal.R.style.Animation_Toast;
+
+ WindowManagerImpl.getDefault().addView(mNotificationPeekWindow, lp);
+
// System Panel
mSystemPanel = (SystemPanel) View.inflate(context, R.layout.sysbar_panel_system, null);
mSystemPanel.setVisibility(View.GONE);
@@ -238,9 +288,13 @@
mDoNotDisturbButton = (TextView)mNotificationButtons.findViewById(R.id.do_not_disturb);
mDoNotDisturbButton.setOnClickListener(mOnClickListener);
-
// where the icons go
mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons);
+ mIconLayout.setOnTouchListener(new NotificationIconTouchListener());
+
+ ViewConfiguration vc = ViewConfiguration.get(context);
+ mNotificationPeekTapDuration = vc.getTapTimeout();
+ mNotificationFlingVelocity = 300; // px/s
mTicker = new TabletTicker(context, (FrameLayout)sb.findViewById(R.id.ticker));
@@ -253,6 +307,9 @@
mNavigationArea = sb.findViewById(R.id.navigationArea);
mMenuButton = mNavigationArea.findViewById(R.id.menu);
+ // The bar contents buttons
+ mInputMethodButton = (InputMethodButton) mBarContents.findViewById(R.id.imeButton);
+
// set the initial view visibility
setAreThereNotifications();
refreshNotificationTrigger();
@@ -276,9 +333,57 @@
private class H extends Handler {
public void handleMessage(Message m) {
switch (m.what) {
+ case MSG_OPEN_NOTIFICATION_PEEK:
+ if (DEBUG) Slog.d(TAG, "opening notification peek window; arg=" + m.arg1);
+ if (m.arg1 >= 0) {
+ final int N = mNotns.size();
+ if (mNotificationPeekIndex < N) {
+ NotificationData.Entry entry = mNotns.get(N-1-mNotificationPeekIndex);
+ entry.icon.setBackgroundColor(0);
+ }
+
+ final int peekIndex = m.arg1;
+ if (peekIndex < N) {
+ Slog.d(TAG, "loading peek: " + peekIndex);
+ NotificationData.Entry entry = mNotns.get(N-1-peekIndex);
+ NotificationData.Entry copy = new NotificationData.Entry(
+ entry.key,
+ entry.notification,
+ entry.icon);
+ inflateViews(copy, mNotificationPeekRow);
+
+ entry.icon.setBackgroundColor(0x20FFFFFF);
+
+// mNotificationPeekRow.setLayoutTransition(
+// peekIndex < mNotificationPeekIndex
+// ? mNotificationPeekScrubLeft
+// : mNotificationPeekScrubRight);
+
+ mNotificationPeekRow.removeAllViews();
+ mNotificationPeekRow.addView(copy.row);
+
+ mNotificationPeekWindow.setVisibility(View.VISIBLE);
+ mNotificationPanel.setVisibility(View.GONE);
+
+ mNotificationPeekIndex = peekIndex;
+ }
+ }
+ break;
+ case MSG_CLOSE_NOTIFICATION_PEEK:
+ if (DEBUG) Slog.d(TAG, "closing notification peek window");
+ mNotificationPeekWindow.setVisibility(View.GONE);
+ mNotificationPeekRow.removeAllViews();
+ final int N = mNotns.size();
+ if (mNotificationPeekIndex < N) {
+ NotificationData.Entry entry = mNotns.get(N-1-mNotificationPeekIndex);
+ entry.icon.setBackgroundColor(0);
+ }
+ break;
case MSG_OPEN_NOTIFICATION_PANEL:
if (DEBUG) Slog.d(TAG, "opening notifications panel");
if (mNotificationPanel.getVisibility() == View.GONE) {
+ mNotificationPeekWindow.setVisibility(View.GONE);
+
mDoNotDisturbButton.setText(mNotificationsOn
? R.string.status_bar_do_not_disturb_button
: R.string.status_bar_please_disturb_button);
@@ -336,7 +441,7 @@
}
mNotificationTrigger.setImageResource(resId);
}
-
+
public void setBatteryMeter(int level, boolean plugged) {
if (DEBUG) Slog.d(TAG, "battery=" + level + (plugged ? " - plugged" : " - unplugged"));
mBatteryMeter.setImageResource(R.drawable.sysbar_batterymini);
@@ -590,6 +695,14 @@
visible ? R.anim.navigation_in : R.anim.navigation_out);
}
+ public void setIMEButtonVisible(boolean visible) {
+
+ if (DEBUG) {
+ Slog.d(TAG, (visible?"showing":"hiding") + " the IME button");
+ }
+ mInputMethodButton.setIMEButtonVisible(visible);
+ }
+
private void setAreThereNotifications() {
final boolean hasClearable = mNotns.hasClearableItems();
@@ -812,6 +925,72 @@
return entry.notification;
}
+ private class NotificationIconTouchListener implements View.OnTouchListener {
+ VelocityTracker mVT;
+
+ public NotificationIconTouchListener() {
+ }
+
+ public boolean onTouch(View v, MotionEvent event) {
+ boolean peeking = mNotificationPeekWindow.getVisibility() != View.GONE;
+ boolean panelShowing = mNotificationPanel.getVisibility() != View.GONE;
+ if (panelShowing) return false;
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mVT = VelocityTracker.obtain();
+
+ // fall through
+ case MotionEvent.ACTION_OUTSIDE:
+ case MotionEvent.ACTION_MOVE:
+ // peek and switch icons if necessary
+ int numIcons = mIconLayout.getChildCount();
+ int peekIndex =
+ (int)((float)event.getX() * numIcons / mIconLayout.getWidth());
+ if (peekIndex > numIcons - 1) peekIndex = numIcons - 1;
+ else if (peekIndex < 0) peekIndex = 0;
+
+ if (!peeking || mNotificationPeekIndex != peekIndex) {
+ if (DEBUG) Slog.d(TAG, "will peek at notification #" + peekIndex);
+ Message peekMsg = mHandler.obtainMessage(MSG_OPEN_NOTIFICATION_PEEK);
+ peekMsg.arg1 = peekIndex;
+
+ mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK);
+
+ // no delay if we're scrubbing left-right
+ mHandler.sendMessageDelayed(peekMsg,
+ peeking ? 0 : mNotificationPeekTapDuration);
+ }
+
+ // check for fling
+ if (mVT != null) {
+ mVT.addMovement(event);
+ mVT.computeCurrentVelocity(1000);
+ // require a little more oomph once we're already in peekaboo mode
+ if (!panelShowing && (
+ (peeking && mVT.getYVelocity() < -mNotificationFlingVelocity*3)
+ || (mVT.getYVelocity() < -mNotificationFlingVelocity))) {
+ mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK);
+ mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PANEL);
+ mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PEEK);
+ mHandler.sendEmptyMessage(MSG_OPEN_NOTIFICATION_PANEL);
+ }
+ }
+ return true;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK);
+ if (peeking) {
+ mHandler.sendEmptyMessageDelayed(MSG_CLOSE_NOTIFICATION_PEEK, 250);
+ }
+ mVT.recycle();
+ mVT = null;
+ return true;
+ }
+ return false;
+ }
+ }
+
StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
if (DEBUG) {
Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
@@ -836,6 +1015,7 @@
+ notification);
return null;
}
+
// Add the icon.
mNotns.add(entry);
refreshIcons();
@@ -847,28 +1027,76 @@
// XXX: need to implement a new limited linear layout class
// to avoid removing & readding everything
+ final int ICON_LIMIT = 4;
+ final LinearLayout.LayoutParams params
+ = new LinearLayout.LayoutParams(mIconSize, mIconSize);
+
int N = mNotns.size();
- LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mIconSize, mIconSize);
if (DEBUG) {
- Slog.d(TAG, "refreshing icons (" + N + " notifications, mIconLayout="
- + mIconLayout + ", mPile=" + mPile);
+ Slog.d(TAG, "refreshing icons: " + N + " notifications, mIconLayout=" + mIconLayout);
}
- mIconLayout.removeAllViews();
- for (int i=0; i<4; i++) {
+ ArrayList<View> toShow = new ArrayList<View>();
+
+ for (int i=0; i<ICON_LIMIT; i++) {
if (i>=N) break;
- mIconLayout.addView(mNotns.get(N-i-1).icon, i, params);
+ toShow.add(mNotns.get(N-i-1).icon);
}
- mPile.removeAllViews();
- for (int i=0; i<N; i++) {
- mPile.addView(mNotns.get(N-i-1).row);
+ ArrayList<View> toRemove = new ArrayList<View>();
+ for (int i=0; i<mIconLayout.getChildCount(); i++) {
+ View child = mIconLayout.getChildAt(i);
+ if (!toShow.contains(child)) {
+ toRemove.add(child);
+ }
}
+ for (View remove : toRemove) {
+ mIconLayout.removeView(remove);
+ }
+
+ for (int i=0; i<toShow.size(); i++) {
+ View v = toShow.get(i);
+ if (v.getParent() == null) {
+ mIconLayout.addView(toShow.get(i), i, params);
+ }
+ }
+
+ loadNotificationPanel();
refreshNotificationTrigger();
}
+ private void loadNotificationPanel() {
+ int N = mNotns.size();
+
+ ArrayList<View> toShow = new ArrayList<View>();
+
+ for (int i=0; i<N; i++) {
+ View row = mNotns.get(N-i-1).row;
+ toShow.add(row);
+ }
+
+ ArrayList<View> toRemove = new ArrayList<View>();
+ for (int i=0; i<mPile.getChildCount(); i++) {
+ View child = mPile.getChildAt(i);
+ if (!toShow.contains(child)) {
+ toRemove.add(child);
+ }
+ }
+
+ for (View remove : toRemove) {
+ mPile.removeView(remove);
+ }
+
+ for (int i=0; i<toShow.size(); i++) {
+ View v = toShow.get(i);
+ if (v.getParent() == null) {
+ mPile.addView(toShow.get(i));
+ }
+ }
+ }
+
private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
StatusBarNotification sbn = entry.notification;
RemoteViews remoteViews = sbn.notification.contentView;
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 3f378e1..07da0fa 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -940,6 +940,23 @@
}
}
+ public void setIMEButtonVisible(IBinder token, boolean visible) {
+ int uid = Binder.getCallingUid();
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (token == null || mCurToken != token) {
+ Slog.w(TAG, "Ignoring setIMEButtonVisible of uid " + uid + " token: " + token);
+ return;
+ }
+
+ synchronized (mMethodMap) {
+ mStatusBar.setIMEButtonVisible(visible);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
void updateFromSettingsLocked() {
// We are assuming that whoever is changing DEFAULT_INPUT_METHOD and
// ENABLED_INPUT_METHODS is taking care of keeping them correctly in
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index 400b31f..95d2b65 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -74,6 +74,8 @@
boolean mMenuVisible = false;
+ boolean mIMEButtonVisible = false;
+
private class DisableRecord implements IBinder.DeathRecipient {
String pkg;
int what;
@@ -257,6 +259,28 @@
}
}
+ public void setIMEButtonVisible(final boolean visible) {
+ enforceStatusBar();
+
+ if (SPEW) Slog.d(TAG, (visible?"showing":"hiding") + " IME Button");
+
+ synchronized(mLock) {
+ if (mIMEButtonVisible != visible) {
+ mIMEButtonVisible = visible;
+ mHandler.post(new Runnable() {
+ public void run() {
+ if (mBar != null) {
+ try {
+ mBar.setIMEButtonVisible(visible);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ });
+ }
+ }
+ }
+
/**
* This is used for the automatic version of lights-out mode. Only call this from
* the window manager.
@@ -345,6 +369,7 @@
synchronized (mLock) {
switches[0] = mLightsOn;
switches[1] = mMenuVisible;
+ switches[2] = mIMEButtonVisible;
}
}
diff --git a/tests/StatusBar/res/drawable-mdpi/icon1.png b/tests/StatusBar/res/drawable-mdpi/icon1.png
index abfb6fa..a999834 100644
--- a/tests/StatusBar/res/drawable-mdpi/icon1.png
+++ b/tests/StatusBar/res/drawable-mdpi/icon1.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/icon2.png b/tests/StatusBar/res/drawable-mdpi/icon2.png
index 564b38b..b7763cb 100644
--- a/tests/StatusBar/res/drawable-mdpi/icon2.png
+++ b/tests/StatusBar/res/drawable-mdpi/icon2.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification0.png b/tests/StatusBar/res/drawable-mdpi/notification0.png
new file mode 100644
index 0000000..39794f3
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification0.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification1.png b/tests/StatusBar/res/drawable-mdpi/notification1.png
new file mode 100644
index 0000000..b7bbe2b
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification1.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification2.png b/tests/StatusBar/res/drawable-mdpi/notification2.png
new file mode 100644
index 0000000..b014280
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification2.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification3.png b/tests/StatusBar/res/drawable-mdpi/notification3.png
new file mode 100644
index 0000000..2c618211
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification3.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification4.png b/tests/StatusBar/res/drawable-mdpi/notification4.png
new file mode 100644
index 0000000..1e04793
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification4.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification5.png b/tests/StatusBar/res/drawable-mdpi/notification5.png
new file mode 100644
index 0000000..f05dfe5
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification5.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification6.png b/tests/StatusBar/res/drawable-mdpi/notification6.png
new file mode 100644
index 0000000..b807181
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification6.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification7.png b/tests/StatusBar/res/drawable-mdpi/notification7.png
new file mode 100644
index 0000000..63b2544
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification7.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification8.png b/tests/StatusBar/res/drawable-mdpi/notification8.png
new file mode 100644
index 0000000..13c1499
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification8.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification9.png b/tests/StatusBar/res/drawable-mdpi/notification9.png
new file mode 100644
index 0000000..587d04c
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification9.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notificationx.png b/tests/StatusBar/res/drawable-mdpi/notificationx.png
new file mode 100644
index 0000000..4df9804
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notificationx.png
Binary files differ
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 9c267d6..38ee1b8 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -44,6 +44,20 @@
long mActivityCreateTime = System.currentTimeMillis();
long mChronometerBase = 0;
+ final int[] kNumberedIconResIDs = {
+ R.drawable.notification0,
+ R.drawable.notification1,
+ R.drawable.notification2,
+ R.drawable.notification3,
+ R.drawable.notification4,
+ R.drawable.notification5,
+ R.drawable.notification6,
+ R.drawable.notification7,
+ R.drawable.notification8,
+ R.drawable.notification9
+ };
+ final int kUnnumberedIconResID = R.drawable.notificationx;
+
@Override
protected String tag() {
return TAG;
@@ -708,7 +722,8 @@
new Test("Ten Notifications") {
public void run() {
for (int i = 0; i < 2; i++) {
- Notification n = new Notification(NotificationTestList.this, R.drawable.icon2,
+ Notification n = new Notification(NotificationTestList.this,
+ kNumberedIconResIDs[i],
null, System.currentTimeMillis(), "Persistent #" + i,
"Notify me!!!" + i, null);
n.flags |= Notification.FLAG_ONGOING_EVENT;
@@ -716,7 +731,8 @@
mNM.notify((i+1)*10, n);
}
for (int i = 2; i < 10; i++) {
- Notification n = new Notification(NotificationTestList.this, R.drawable.icon2,
+ Notification n = new Notification(NotificationTestList.this,
+ kNumberedIconResIDs[i],
null, System.currentTimeMillis(), "Persistent #" + i,
"Notify me!!!" + i, null);
n.number = i;
@@ -733,6 +749,13 @@
}
},
+ new Test("Cancel the other two notifications") {
+ public void run() {
+ mNM.cancel(10);
+ mNM.cancel(100);
+ }
+ },
+
new Test("Persistent with numbers 1") {
public void run() {
mNM.notify(1, notificationWithNumbers(1));
@@ -776,8 +799,14 @@
};
private Notification notificationWithNumbers(int num) {
- Notification n = new Notification(this, R.drawable.icon2, null, System.currentTimeMillis(),
- "Persistent #2", "Notify me!!!", null);
+ Notification n = new Notification(this,
+ (num >= 0 && num < kNumberedIconResIDs.length)
+ ? kNumberedIconResIDs[num]
+ : kUnnumberedIconResID,
+ null,
+ System.currentTimeMillis(),
+ "Notification", "Number=" + num,
+ null);
n.number = num;
return n;
}
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index 0179748..f275e39 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -37,20 +37,19 @@
import java.util.Map;
/**
- * Class that handles an Internet audio call over SIP. {@link SipManager}
- * facilitates instantiating a {@code SipAudioCall} object for making/receiving
- * calls. See {@link SipManager#makeAudioCall} and
- * {@link SipManager#takeAudioCall}.
+ * Handles an Internet audio call over SIP. You can instantiate this class with {@link SipManager},
+ * using {@link SipManager#makeAudioCall makeAudioCall()} and {@link SipManager#takeAudioCall
+ * takeAudioCall()}.
*
- * <p>Requires permissions to use this class:
+ * <p class="note"><strong>Note:</strong> Using this class require the
* {@link android.Manifest.permission#INTERNET} and
- * {@link android.Manifest.permission#USE_SIP}.
- * <br/>Requires permissions to {@link #startAudio}:
+ * {@link android.Manifest.permission#USE_SIP} permissions.<br/><br/>In addition, {@link
+ * #startAudio} requires the
* {@link android.Manifest.permission#RECORD_AUDIO},
- * {@link android.Manifest.permission#ACCESS_WIFI_STATE} and
- * {@link android.Manifest.permission#WAKE_LOCK}.
- * <br/>Requires permissions to {@link #setSpeakerMode}:
- * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
+ * {@link android.Manifest.permission#ACCESS_WIFI_STATE}, and
+ * {@link android.Manifest.permission#WAKE_LOCK} permissions; and {@link #setSpeakerMode
+ * setSpeakerMode()} requires the
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS} permission.</p>
*/
public class SipAudioCall {
private static final String TAG = SipAudioCall.class.getSimpleName();
@@ -58,7 +57,10 @@
private static final boolean DONT_RELEASE_SOCKET = false;
private static final int SESSION_TIMEOUT = 5; // in seconds
- /** Listener class for all event callbacks. */
+ /** Listener for events relating to a SIP call, such as when a call is being
+ * recieved ("on ringing") or a call is outgoing ("on calling").
+ * <p>Many of these events are also received by {@link SipSession.Listener}.</p>
+ */
public static class Listener {
/**
* Called when the call object is ready to make another call.
@@ -199,7 +201,7 @@
/**
* Sets the listener to listen to the audio call events. The method calls
- * {@code setListener(listener, false)}.
+ * {@link #setListener setListener(listener, false)}.
*
* @param listener to listen to the audio call events of this object
* @see #setListener(Listener, boolean)
@@ -537,14 +539,14 @@
/**
* Initiates an audio call to the specified profile. The attempt will be
* timed out if the call is not established within {@code timeout} seconds
- * and {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
+ * and {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called.
*
* @param peerProfile the SIP profile to make the call to
* @param sipSession the {@link SipSession} for carrying out the call
* @param timeout the timeout value in seconds. Default value (defined by
* SIP protocol) is used if {@code timeout} is zero or negative.
- * @see Listener.onError
+ * @see Listener#onError
* @throws SipException if the SIP service fails to create a session for the
* call
*/
@@ -582,12 +584,12 @@
* Puts a call on hold. When succeeds, {@link Listener#onCallHeld} is
* called. The attempt will be timed out if the call is not established
* within {@code timeout} seconds and
- * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
+ * {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called.
*
* @param timeout the timeout value in seconds. Default value (defined by
* SIP protocol) is used if {@code timeout} is zero or negative.
- * @see Listener.onError
+ * @see Listener#onError
* @throws SipException if the SIP service fails to hold the call
*/
public void holdCall(int timeout) throws SipException {
@@ -604,12 +606,12 @@
/**
* Answers a call. The attempt will be timed out if the call is not
* established within {@code timeout} seconds and
- * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
+ * {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called.
*
* @param timeout the timeout value in seconds. Default value (defined by
* SIP protocol) is used if {@code timeout} is zero or negative.
- * @see Listener.onError
+ * @see Listener#onError
* @throws SipException if the SIP service fails to answer the call
*/
public void answerCall(int timeout) throws SipException {
@@ -628,12 +630,12 @@
* Continues a call that's on hold. When succeeds,
* {@link Listener#onCallEstablished} is called. The attempt will be timed
* out if the call is not established within {@code timeout} seconds and
- * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
+ * {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called.
*
* @param timeout the timeout value in seconds. Default value (defined by
* SIP protocol) is used if {@code timeout} is zero or negative.
- * @see Listener.onError
+ * @see Listener#onError
* @throws SipException if the SIP service fails to unhold the call
*/
public void continueCall(int timeout) throws SipException {
@@ -788,8 +790,8 @@
/**
* Puts the device to speaker mode.
- * <p>Requires permission:
- * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
+ * <p class="note"><strong>Note:</strong> Requires the
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS} permission.</p>
*/
public void setSpeakerMode(boolean speakerMode) {
synchronized (this) {
@@ -799,20 +801,21 @@
}
/**
- * Sends a DTMF code. According to RFC2833, event 0--9 maps to decimal
+ * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2883</a>,
+ * event 0--9 maps to decimal
* value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event
* flash to 16. Currently, event flash is not supported.
*
* @param code the DTMF code to send. Value 0 to 15 (inclusive) are valid
* inputs.
- * @see http://tools.ietf.org/html/rfc2833
*/
public void sendDtmf(int code) {
sendDtmf(code, null);
}
/**
- * Sends a DTMF code. According to RFC2833, event 0--9 maps to decimal
+ * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2883</a>,
+ * event 0--9 maps to decimal
* value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event
* flash to 16. Currently, event flash is not supported.
*
@@ -890,10 +893,10 @@
/**
* Starts the audio for the established call. This method should be called
* after {@link Listener#onCallEstablished} is called.
- * <p>Requires permission:
+ * <p class="note"><strong>Note:</strong> Requires the
* {@link android.Manifest.permission#RECORD_AUDIO},
* {@link android.Manifest.permission#ACCESS_WIFI_STATE} and
- * {@link android.Manifest.permission#WAKE_LOCK}.
+ * {@link android.Manifest.permission#WAKE_LOCK} permissions.</p>
*/
public void startAudio() {
try {
diff --git a/voip/java/android/net/sip/SipErrorCode.java b/voip/java/android/net/sip/SipErrorCode.java
index 6aee5f1..509728f 100644
--- a/voip/java/android/net/sip/SipErrorCode.java
+++ b/voip/java/android/net/sip/SipErrorCode.java
@@ -17,11 +17,11 @@
package android.net.sip;
/**
- * Defines error code returned in
- * {@link SipRegistrationListener#onRegistrationFailed},
- * {@link SipSession.Listener#onError},
- * {@link SipSession.Listener#onCallChangeFailed} and
- * {@link SipSession.Listener#onRegistrationFailed}.
+ * Defines error codes returned during SIP actions. For example, during
+ * {@link SipRegistrationListener#onRegistrationFailed onRegistrationFailed()},
+ * {@link SipSession.Listener#onError onError()},
+ * {@link SipSession.Listener#onCallChangeFailed onCallChangeFailed()} and
+ * {@link SipSession.Listener#onRegistrationFailed onRegistrationFailed()}.
*/
public class SipErrorCode {
/** Not an error. */
diff --git a/voip/java/android/net/sip/SipException.java b/voip/java/android/net/sip/SipException.java
index 225b94f..0339395 100644
--- a/voip/java/android/net/sip/SipException.java
+++ b/voip/java/android/net/sip/SipException.java
@@ -17,7 +17,7 @@
package android.net.sip;
/**
- * General SIP-related exception class.
+ * Indicates a general SIP-related exception.
*/
public class SipException extends Exception {
public SipException() {
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index 38d2b0c..8aaa805 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -29,30 +29,29 @@
import java.text.ParseException;
/**
- * The class provides API for various SIP related tasks. Specifically, the API
- * allows an application to:
+ * Provides APIs for SIP tasks, such as initiating SIP connections, and provides access to related
+ * SIP services. This class is the starting point for any SIP actions. You can acquire an instance
+ * of it with {@link #newInstance newInstance()}.</p>
+ * <p>The APIs in this class allows you to:</p>
* <ul>
- * <li>open a {@link SipProfile} to get ready for making outbound calls or have
- * the background SIP service listen to incoming calls and broadcast them
- * with registered command string. See
- * {@link #open(SipProfile, PendingIntent, SipRegistrationListener)},
- * {@link #open(SipProfile)}, {@link #close}, {@link #isOpened} and
- * {@link #isRegistered}. It also facilitates handling of the incoming call
- * broadcast intent. See
- * {@link #isIncomingCallIntent}, {@link #getCallId},
- * {@link #getOfferSessionDescription} and {@link #takeAudioCall}.</li>
- * <li>make/take SIP-based audio calls. See
- * {@link #makeAudioCall} and {@link #takeAudioCall}.</li>
- * <li>register/unregister with a SIP service provider manually. See
- * {@link #register} and {@link #unregister}.</li>
- * <li>process SIP events directly with a {@link SipSession} created by
- * {@link #createSipSession}.</li>
+ * <li>Create a {@link SipSession} to get ready for making calls or listen for incoming calls. See
+ * {@link #createSipSession createSipSession()} and {@link #getSessionFor getSessionFor()}.</li>
+ * <li>Initiate and receive generic SIP calls or audio-only SIP calls. Generic SIP calls may
+ * be video, audio, or other, and are initiated with {@link #open open()}. Audio-only SIP calls
+ * should be handled with a {@link SipAudioCall}, which you can acquire with {@link
+ * #makeAudioCall makeAudioCall()} and {@link #takeAudioCall takeAudioCall()}.</li>
+ * <li>Register and unregister with a SIP service provider, with
+ * {@link #register register()} and {@link #unregister unregister()}.</li>
+ * <li>Verify session connectivity, with {@link #isOpened isOpened()} and
+ * {@link #isRegistered isRegistered()}.</li>
* </ul>
- * {@code SipManager} can only be instantiated if SIP API is supported by the
- * device. (See {@link #isApiSupported}).
- * <p>Requires permissions to use this class:
- * {@link android.Manifest.permission#INTERNET} and
- * {@link android.Manifest.permission#USE_SIP}.
+ * <p class="note"><strong>Note:</strong> Not all Android-powered devices support VOIP calls using
+ * SIP. You should always call {@link android.net.sip.SipManager#isVoipSupported
+ * isVoipSupported()} to verify that the device supports VOIP calling and {@link
+ * android.net.sip.SipManager#isApiSupported isApiSupported()} to verify that the device supports
+ * the SIP APIs.<br/><br/>Your application must also request the {@link
+ * android.Manifest.permission#INTERNET} and {@link android.Manifest.permission#USE_SIP}
+ * permissions.</p>
*/
public class SipManager {
/**
@@ -160,7 +159,7 @@
}
/**
- * Opens the profile for making calls. The caller may make subsequent calls
+ * Opens the profile for making generic SIP calls. The caller may make subsequent calls
* through {@link #makeAudioCall}. If one also wants to receive calls on the
* profile, use
* {@link #open(SipProfile, PendingIntent, SipRegistrationListener)}
@@ -179,7 +178,7 @@
}
/**
- * Opens the profile for making calls and/or receiving calls. The caller may
+ * Opens the profile for making calls and/or receiving generic SIP calls. The caller may
* make subsequent calls through {@link #makeAudioCall}. If the
* auto-registration option is enabled in the profile, the SIP service
* will register the profile to the corresponding SIP provider periodically
@@ -296,7 +295,7 @@
/**
* Creates a {@link SipAudioCall} to make a call. The attempt will be timed
* out if the call is not established within {@code timeout} seconds and
- * {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
+ * {@link SipAudioCall.Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called.
*
* @param localProfile the SIP profile to make the call from
@@ -307,7 +306,7 @@
* SIP protocol) is used if {@code timeout} is zero or negative.
* @return a {@link SipAudioCall} object
* @throws SipException if calling the SIP service results in an error
- * @see SipAudioCall.Listener.onError
+ * @see SipAudioCall.Listener#onError
*/
public SipAudioCall makeAudioCall(SipProfile localProfile,
SipProfile peerProfile, SipAudioCall.Listener listener, int timeout)
@@ -327,7 +326,7 @@
* Creates a {@link SipAudioCall} to make an audio call. The attempt will be
* timed out if the call is not established within {@code timeout} seconds
* and
- * {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
+ * {@link SipAudioCall.Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called.
*
* @param localProfileUri URI of the SIP profile to make the call from
@@ -338,7 +337,7 @@
* SIP protocol) is used if {@code timeout} is zero or negative.
* @return a {@link SipAudioCall} object
* @throws SipException if calling the SIP service results in an error
- * @see SipAudioCall.Listener.onError
+ * @see SipAudioCall.Listener#onError
*/
public SipAudioCall makeAudioCall(String localProfileUri,
String peerProfileUri, SipAudioCall.Listener listener, int timeout)
@@ -449,7 +448,7 @@
* receiving calls.
* {@link #open(SipProfile, PendingIntent, SipRegistrationListener)} is
* still needed to be called at least once in order for the SIP service to
- * notify the caller with the {@code PendingIntent} when an incoming call is
+ * notify the caller with the {@link android.app.PendingIntent} when an incoming call is
* received.
*
* @param localProfile the SIP profile to register with
diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java
index dddb07d..6977e30 100644
--- a/voip/java/android/net/sip/SipProfile.java
+++ b/voip/java/android/net/sip/SipProfile.java
@@ -32,7 +32,10 @@
import javax.sip.address.URI;
/**
- * Class containing a SIP account, domain and server information.
+ * Defines a SIP profile, including a SIP account, domain and server information.
+ * <p>You can create a {@link SipProfile} using {@link
+ * SipProfile.Builder}. You can also retrieve one from a {@link SipSession}, using {@link
+ * SipSession#getLocalProfile} and {@link SipSession#getPeerProfile}.</p>
*/
public class SipProfile implements Parcelable, Serializable, Cloneable {
private static final long serialVersionUID = 1L;
@@ -59,7 +62,7 @@
};
/**
- * Class to help create a {@code SipProfile}.
+ * Helper class for creating a {@link SipProfile}.
*/
public static class Builder {
private AddressFactory mAddressFactory;
diff --git a/voip/java/android/net/sip/SipRegistrationListener.java b/voip/java/android/net/sip/SipRegistrationListener.java
index e1f35ad..9968cc7 100644
--- a/voip/java/android/net/sip/SipRegistrationListener.java
+++ b/voip/java/android/net/sip/SipRegistrationListener.java
@@ -17,7 +17,7 @@
package android.net.sip;
/**
- * Listener class to listen to SIP registration events.
+ * Listener for SIP registration events.
*/
public interface SipRegistrationListener {
/**
diff --git a/voip/java/android/net/sip/SipSession.java b/voip/java/android/net/sip/SipSession.java
index 9c08e46..5629b3c 100644
--- a/voip/java/android/net/sip/SipSession.java
+++ b/voip/java/android/net/sip/SipSession.java
@@ -20,14 +20,17 @@
import android.util.Log;
/**
- * A SIP session that is associated with a SIP dialog or a standalone
+ * Represents a SIP session that is associated with a SIP dialog or a standalone
* transaction not within a dialog.
+ * <p>You can get a {@link SipSession} from {@link SipManager} with {@link
+ * SipManager#createSipSession createSipSession()} (when initiating calls) or {@link
+ * SipManager#getSessionFor getSessionFor()} (when receiving calls).</p>
*/
public final class SipSession {
private static final String TAG = "SipSession";
/**
- * Defines {@link SipSession} states.
+ * Defines SIP session states, such as "registering", "outgoing call", and "in call".
*/
public static class State {
/** When session is ready to initiate a call or transaction. */
@@ -98,7 +101,9 @@
}
/**
- * Listener class that listens to {@link SipSession} events.
+ * Listener for events relating to a SIP session, such as when a session is being registered
+ * ("on registering") or a call is outgoing ("on calling").
+ * <p>Many of these events are also received by {@link SipAudioCall.Listener}.</p>
*/
public static class Listener {
/**
diff --git a/voip/java/android/net/sip/package.html b/voip/java/android/net/sip/package.html
new file mode 100644
index 0000000..790656b
--- /dev/null
+++ b/voip/java/android/net/sip/package.html
@@ -0,0 +1,39 @@
+<HTML>
+<BODY>
+<p>Provides access to Session Initiation Protocol (SIP) functionality, such as
+making and answering VOIP calls using SIP.</p>
+
+<p>To get started, you need to get an instance of the {@link android.net.sip.SipManager} by
+calling {@link android.net.sip.SipManager#newInstance newInstance()}.</p>
+
+<p>With the {@link android.net.sip.SipManager}, you can initiate SIP audio calls with {@link
+android.net.sip.SipManager#makeAudioCall makeAudioCall()} and {@link
+android.net.sip.SipManager#takeAudioCall takeAudioCall()}. Both methods require
+a {@link android.net.sip.SipAudioCall.Listener} that receives callbacks when the state of the
+call changes, such as when the call is ringing, established, or ended.</p>
+
+<p>Both {@link android.net.sip.SipManager#makeAudioCall makeAudioCall()} also requires two
+{@link android.net.sip.SipProfile} objects, representing the local device and the peer
+device. You can create a {@link android.net.sip.SipProfile} using the {@link
+android.net.sip.SipProfile.Builder} subclass.</p>
+
+<p>Once you have a {@link android.net.sip.SipAudioCall}, you can perform SIP audio call actions with
+the instance, such as make a call, answer a call, mute a call, turn on speaker mode, send DTMF
+tones, and more.</p>
+
+<p>If you want to create generic SIP connections (such as for video calls or other), you can
+create a SIP connection from the {@link android.net.sip.SipManager}, using {@link
+android.net.sip.SipManager#open open()}. If you only want to create audio SIP calls, though, you
+should use the {@link android.net.sip.SipAudioCall} class, as described above.</p>
+
+<p class="note"><strong>Note:</strong>
+Not all Android-powered devices support VOIP functionality with SIP. Before performing any SIP
+activity, you should call {@link android.net.sip.SipManager#isVoipSupported isVoipSupported()}
+to verify that the device supports VOIP calling and {@link
+android.net.sip.SipManager#isApiSupported isApiSupported()} to verify that the device supports the
+SIP APIs.<br/><br/>
+Your application must also request the {@link android.Manifest.permission#INTERNET} and {@link
+android.Manifest.permission#USE_SIP} permissions in order to use the SIP APIs.
+</p>
+</BODY>
+</HTML>
\ No newline at end of file