diff options
Diffstat (limited to 'core/java/android')
27 files changed, 1294 insertions, 158 deletions
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 495156e..5af08f7 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -1129,6 +1129,15 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** + * We never allow ACTV to automatically replace the text, since we use "jamSuggestionQuery" + * to do that. There's no point in letting ACTV do this here, because in the search UI, + * as soon as we click a suggestion, we're going to start shutting things down. + */ + @Override + public void replaceText(CharSequence text) { + } + + /** * We always return true, so that the effective threshold is "zero". This allows us * to provide "null" suggestions such as "just show me some recent entries". */ diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index 022a87c..b0b0154 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -72,6 +72,12 @@ public class BluetoothA2dp { /** Playing implies connected */ public static final int STATE_PLAYING = 4; + /** Default priority for a2dp devices that should allow incoming + * connections */ + public static final int PRIORITY_AUTO = 100; + /** Default priority for a2dp devices that should not allow incoming + * connections */ + public static final int PRIORITY_OFF = 0; private final IBluetoothA2dp mService; private final Context mContext; @@ -158,6 +164,66 @@ public class BluetoothA2dp { } } + /** + * Set priority of a2dp sink. + * Priority is a non-negative integer. By default paired sinks will have + * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0). + * Sinks with priority greater than zero will accept incoming connections + * (if no sink is currently connected). + * Priority for unpaired sink must be PRIORITY_NONE. + * @param address Paired sink + * @param priority Integer priority, for example PRIORITY_AUTO or + * PRIORITY_NONE + * @return Result code, negative indicates an error + */ + public int setSinkPriority(String address, int priority) { + try { + return mService.setSinkPriority(address, priority); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return BluetoothError.ERROR_IPC; + } + } + + /** + * Get priority of a2dp sink. + * @param address Sink + * @return non-negative priority, or negative error code on error. + */ + public int getSinkPriority(String address) { + try { + return mService.getSinkPriority(address); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return BluetoothError.ERROR_IPC; + } + } + + /** + * Check class bits for possible A2DP Sink support. + * This is a simple heuristic that tries to guess if a device with the + * given class bits might be a A2DP Sink. It is not accurate for all + * devices. It tries to err on the side of false positives. + * @return True if this device might be a A2DP sink + */ + public static boolean doesClassMatchSink(int btClass) { + if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { + return true; + } + // By the A2DP spec, sinks must indicate the RENDER service. + // However we found some that do not (Chordette). So lets also + // match on some other class bits. + switch (BluetoothClass.Device.getDevice(btClass)) { + case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: + case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: + case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER: + case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: + return true; + default: + return false; + } + } + /** Helper for converting a state to a string. * For debug use only - strings are not internationalized. * @hide diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 905173e..c315271 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -57,7 +57,6 @@ public class BluetoothHeadset { private IBluetoothHeadset mService; private final Context mContext; private final ServiceListener mServiceListener; - private ConnectHeadsetCallback mConnectHeadsetCallback; /** There was an error trying to obtain the state */ public static final int STATE_ERROR = -1; @@ -73,6 +72,11 @@ public class BluetoothHeadset { /** Connection cancelled before completetion. */ public static final int RESULT_CANCELLED = 2; + /** Default priority for headsets that should be auto-connected */ + public static final int PRIORITY_AUTO = 100; + /** Default priority for headsets that should not be auto-connected */ + public static final int PRIORITY_OFF = 0; + /** * An interface for notifying BluetoothHeadset IPC clients when they have * been connected to the BluetoothHeadset service. @@ -97,14 +101,6 @@ public class BluetoothHeadset { } /** - * Interface for connectHeadset() callback. - * This callback can occur in the Binder thread. - */ - public interface ConnectHeadsetCallback { - public void onConnectHeadsetResult(String address, int resultCode); - } - - /** * Create a BluetoothHeadset proxy object. */ public BluetoothHeadset(Context context, ServiceListener l) { @@ -175,24 +171,18 @@ public class BluetoothHeadset { * Request to initiate a connection to a headset. * This call does not block. Fails if a headset is already connecting * or connected. - * Will connect to the last connected headset if address is null. - * onConnectHeadsetResult() of your ConnectHeadsetCallback will be called - * on completition. - * @param address The Bluetooth Address to connect to, or null to connect - * to the last connected headset. - * @param callback Callback on result. Not called if false is returned. Can - * be null. - * to the last connected headset. + * Initiates auto-connection if address is null. Tries to connect to all + * devices with priority greater than PRIORITY_AUTO in descending order. + * @param address The Bluetooth Address to connect to, or null to + * auto-connect to the last connected headset. * @return False if there was a problem initiating the connection - * procedure, and your callback will not be used. True if - * the connection procedure was initiated, in which case - * your callback is guarenteed to be called. + * procedure, and no further HEADSET_STATE_CHANGED intents + * will be expected. */ - public boolean connectHeadset(String address, ConnectHeadsetCallback callback) { + public boolean connectHeadset(String address) { if (mService != null) { try { - if (mService.connectHeadset(address, mHeadsetCallback)) { - mConnectHeadsetCallback = callback; + if (mService.connectHeadset(address)) { return true; } } catch (RemoteException e) {Log.e(TAG, e.toString());} @@ -273,6 +263,71 @@ public class BluetoothHeadset { return false; } + /** + * Set priority of headset. + * Priority is a non-negative integer. By default paired headsets will have + * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0). + * Headsets with priority greater than zero will be auto-connected, and + * incoming connections will be accepted (if no other headset is + * connected). + * Auto-connection occurs at the following events: boot, incoming phone + * call, outgoing phone call. + * Headsets with priority equal to zero, or that are unpaired, are not + * auto-connected. + * Incoming connections are ignored regardless of priority if there is + * already a headset connected. + * @param address Paired headset + * @param priority Integer priority, for example PRIORITY_AUTO or + * PRIORITY_NONE + * @return True if successful, false if there was some error. + */ + public boolean setPriority(String address, int priority) { + if (mService != null) { + try { + return mService.setPriority(address, priority); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } + return false; + } + + /** + * Get priority of headset. + * @param address Headset + * @return non-negative priority, or negative error code on error. + */ + public int getPriority(String address) { + if (mService != null) { + try { + return mService.getPriority(address); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } + return -1; + } + + /** + * Check class bits for possible HSP or HFP support. + * This is a simple heuristic that tries to guess if a device with the + * given class bits might support HSP or HFP. It is not accurate for all + * devices. It tries to err on the side of false positives. + * @return True if this device might support HSP or HFP. + */ + public static boolean doesClassMatch(int btClass) { + switch (BluetoothClass.Device.getDevice(btClass)) { + case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: + case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: + case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: + return true; + default: + return false; + } + } + private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); @@ -289,12 +344,4 @@ public class BluetoothHeadset { } } }; - - private IBluetoothHeadsetCallback mHeadsetCallback = new IBluetoothHeadsetCallback.Stub() { - public void onConnectHeadsetResult(String address, int resultCode) { - if (mConnectHeadsetCallback != null) { - mConnectHeadsetCallback.onConnectHeadsetResult(address, resultCode); - } - } - }; } diff --git a/core/java/android/bluetooth/IBluetoothA2dp.aidl b/core/java/android/bluetooth/IBluetoothA2dp.aidl index 7e0226d..55ff27f 100644 --- a/core/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl @@ -26,4 +26,6 @@ interface IBluetoothA2dp { int disconnectSink(in String address); List<String> listConnectedSinks(); int getSinkState(in String address); + int setSinkPriority(in String address, int priority); + int getSinkPriority(in String address); } diff --git a/core/java/android/bluetooth/IBluetoothHeadset.aidl b/core/java/android/bluetooth/IBluetoothHeadset.aidl index 564861f..582d4e3 100644 --- a/core/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl @@ -16,8 +16,6 @@ package android.bluetooth; -import android.bluetooth.IBluetoothHeadsetCallback; - /** * System private API for Bluetooth Headset service * @@ -25,22 +23,12 @@ import android.bluetooth.IBluetoothHeadsetCallback; */ interface IBluetoothHeadset { int getState(); - String getHeadsetAddress(); - - // Request that the given headset be connected - // Assumes the given headset is already bonded - // Will disconnect any currently connected headset - // returns false if cannot start a connection (for example, there is - // already a pending connect). callback will always be called iff this - // returns true - boolean connectHeadset(in String address, in IBluetoothHeadsetCallback callback); - + boolean connectHeadset(in String address); void disconnectHeadset(); - boolean isConnected(in String address); - boolean startVoiceRecognition(); - boolean stopVoiceRecognition(); + boolean setPriority(in String address, int priority); + int getPriority(in String address); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 6da00df..3908aa1 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1249,6 +1249,15 @@ public abstract class Context { public static final String INPUT_METHOD_SERVICE = "input_method"; /** + * Use with {@link #getSystemService} to retrieve a + * {@blink android.gadget.GadgetManager} for accessing wallpapers. + * + * @hide + * @see #getSystemService + */ + public static final String GADGET_SERVICE = "gadget"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/gadget/GadgetHost.java b/core/java/android/gadget/GadgetHost.java new file mode 100644 index 0000000..418f2aa --- /dev/null +++ b/core/java/android/gadget/GadgetHost.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 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. + */ + +package android.gadget; + +import android.content.Context; +import android.widget.RemoteViews; + +/** + * GadgetHost provides the interaction with the Gadget Service for apps, + * like the home screen, that want to embed gadgets in their UI. + */ +public class GadgetHost { + public GadgetHost(Context context, int hostId) { + } + + /** + * Start receiving onGadgetChanged calls for your gadgets. Call this when your activity + * becomes visible, i.e. from onStart() in your Activity. + */ + public void startListening() { + } + + /** + * Stop receiving onGadgetChanged calls for your gadgets. Call this when your activity is + * no longer visible, i.e. from onStop() in your Activity. + */ + public void stopListening() { + } + + /** + * Stop listening to changes for this gadget. + */ + public void gadgetRemoved(int gadgetId) { + } + + /** + * Remove all records about gadget instances from the gadget manager. Call this when + * initializing your database, as it might be because of a data wipe. + */ + public void clearGadgets() { + } + + public final GadgetHostView createView(Context context, int gadgetId, GadgetInfo gadget) { + GadgetHostView view = onCreateView(context, gadgetId, gadget); + view.setGadget(gadgetId, gadget); + view.updateGadget(null); + return view; + } + + /** + * Called to create the GadgetHostView. Override to return a custom subclass if you + * need it. {@more} + */ + protected GadgetHostView onCreateView(Context context, int gadgetId, GadgetInfo gadget) { + return new GadgetHostView(context); + } +} + diff --git a/core/java/android/gadget/GadgetHostView.java b/core/java/android/gadget/GadgetHostView.java new file mode 100644 index 0000000..e2bef8c --- /dev/null +++ b/core/java/android/gadget/GadgetHostView.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2008 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 android.gadget; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.gadget.GadgetInfo; +import android.util.AttributeSet; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.RemoteViews; +import android.widget.TextView; + +public class GadgetHostView extends FrameLayout { + static final String TAG = "GadgetHostView"; + + // When we're inflating the initialLayout for a gadget, we only allow + // views that are allowed in RemoteViews. + static final LayoutInflater.Filter sInflaterFilter = new LayoutInflater.Filter() { + public boolean onLoadClass(Class clazz) { + return clazz.isAnnotationPresent(RemoteViews.RemoteView.class); + } + }; + + int mGadgetId; + GadgetInfo mInfo; + View mContentView; + + public GadgetHostView(Context context) { + super(context); + } + + public void setGadget(int gadgetId, GadgetInfo info) { + if (mInfo != null) { + // TODO: remove the old view, or whatever + } + mGadgetId = gadgetId; + mInfo = info; + } + + public void updateGadget(RemoteViews remoteViews) { + Context context = getContext(); + + View contentView = null; + Exception exception = null; + try { + if (remoteViews == null) { + // there is no remoteViews (yet), so use the initial layout + Context theirContext = context.createPackageContext(mInfo.provider.getPackageName(), + 0); + LayoutInflater inflater = (LayoutInflater)theirContext.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + inflater = inflater.cloneInContext(theirContext); + inflater.setFilter(sInflaterFilter); + contentView = inflater.inflate(mInfo.initialLayout, this, false); + } else { + // use the RemoteViews + contentView = remoteViews.apply(mContext, this); + } + } + catch (PackageManager.NameNotFoundException e) { + exception = e; + } + catch (RuntimeException e) { + exception = e; + } + if (contentView == null) { + Log.w(TAG, "Error inflating gadget " + mInfo, exception); + // TODO: Should we throw an exception here for the host activity to catch? + // Maybe we should show a generic error widget. + TextView tv = new TextView(context); + tv.setText("Error inflating gadget"); + contentView = tv; + } + + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.WRAP_CONTENT, + FrameLayout.LayoutParams.WRAP_CONTENT); + + mContentView = contentView; + this.addView(contentView, lp); + + // TODO: do an animation (maybe even one provided by the gadget). + } +} + diff --git a/core/java/android/bluetooth/IBluetoothHeadsetCallback.aidl b/core/java/android/gadget/GadgetInfo.aidl index 03e884b..7231545 100644 --- a/core/java/android/bluetooth/IBluetoothHeadsetCallback.aidl +++ b/core/java/android/gadget/GadgetInfo.aidl @@ -1,25 +1,19 @@ /* - * Copyright (C) 2008, The Android Open Source Project + * Copyright (c) 2007, 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 + * 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 + * 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 + * 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 android.bluetooth; +package android.gadget; -/** - * {@hide} - */ -oneway interface IBluetoothHeadsetCallback -{ - void onConnectHeadsetResult(in String address, int resultCode); -} +parcelable GadgetInfo; diff --git a/core/java/android/gadget/GadgetInfo.java b/core/java/android/gadget/GadgetInfo.java new file mode 100644 index 0000000..1a7a9a0 --- /dev/null +++ b/core/java/android/gadget/GadgetInfo.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 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. + */ + +package android.gadget; + +import android.os.Parcel; +import android.os.Parcelable; +import android.content.ComponentName; + +/** + * Describes the meta data for an installed gadget. + */ +public class GadgetInfo implements Parcelable { + /** + * Identity of this gadget component. This component should be a {@link + * android.content.BroadcastReceiver}, and it will be sent the Gadget intents + * {@link android.gadget as described in the gadget package documentation}. + */ + public ComponentName provider; + + /** + * Minimum width of the gadget, in dp. + */ + public int minWidth; + + /** + * Minimum height of the gadget, in dp. + */ + public int minHeight; + + /** + * How often, in milliseconds, that this gadget wants to be updated. + * The gadget manager may place a limit on how often a gadget is updated. + */ + public int updatePeriodMillis; + + /** + * The resource id of the initial layout for this gadget. This should be + * displayed until the RemoteViews for the gadget is available. + */ + public int initialLayout; + + /** + * The activity to launch that will configure the gadget. + */ + public ComponentName configure; + + public GadgetInfo() { + } + + /** + * Unflatten the GadgetInfo from a parcel. + */ + public GadgetInfo(Parcel in) { + if (0 != in.readInt()) { + this.provider = new ComponentName(in); + } + this.minWidth = in.readInt(); + this.minHeight = in.readInt(); + this.updatePeriodMillis = in.readInt(); + this.initialLayout = in.readInt(); + if (0 != in.readInt()) { + this.configure = new ComponentName(in); + } + } + + + public void writeToParcel(android.os.Parcel out, int flags) { + if (this.provider != null) { + out.writeInt(1); + this.provider.writeToParcel(out, flags); + } else { + out.writeInt(0); + } + out.writeInt(this.minWidth); + out.writeInt(this.minHeight); + out.writeInt(this.updatePeriodMillis); + out.writeInt(this.initialLayout); + if (this.configure != null) { + out.writeInt(1); + this.configure.writeToParcel(out, flags); + } else { + out.writeInt(0); + } + } + + public int describeContents() { + return 0; + } + + /** + * Parcelable.Creator that instantiates GadgetInfo objects + */ + public static final Parcelable.Creator<GadgetInfo> CREATOR + = new Parcelable.Creator<GadgetInfo>() + { + public GadgetInfo createFromParcel(Parcel parcel) + { + return new GadgetInfo(parcel); + } + + public GadgetInfo[] newArray(int size) + { + return new GadgetInfo[size]; + } + }; + + public String toString() { + return "GadgetInfo(provider=" + this.provider + ")"; + } +} + + diff --git a/core/java/android/gadget/GadgetManager.java b/core/java/android/gadget/GadgetManager.java new file mode 100644 index 0000000..49c706e --- /dev/null +++ b/core/java/android/gadget/GadgetManager.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 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. + */ + +package android.gadget; + +import android.content.ComponentName; +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; +import android.widget.RemoteViews; + +import com.android.internal.gadget.IGadgetService; + +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.WeakHashMap; + +public class GadgetManager { + static final String TAG = "GadgetManager"; + + /** + * Send this when you want to pick a gadget to display. + * + * <p> + * The system will respond with an onActivityResult call with the following extras in + * the intent: + * <ul> + * <li><b>gadgetId</b></li> + * <li><b>gadgetId</b></li> + * <li><b>gadgetId</b></li> + * </ul> + * TODO: Add constants for these. + * TODO: Where does this go? + */ + public static final String GADGET_PICK_ACTION = "android.gadget.action.PICK_GADGET"; + + public static final String EXTRA_GADGET_ID = "gadgetId"; + + /** + * Sent when it is time to update your gadget. + */ + public static final String GADGET_UPDATE_ACTION = "android.gadget.GADGET_UPDATE"; + + /** + * Sent when the gadget is added to a host for the first time. TODO: Maybe we don't want this. + */ + public static final String GADGET_ENABLE_ACTION = "android.gadget.GADGET_ENABLE"; + + /** + * Sent when the gadget is removed from the last host. TODO: Maybe we don't want this. + */ + public static final String GADGET_DISABLE_ACTION = "android.gadget.GADGET_DISABLE"; + + static WeakHashMap<Context, WeakReference<GadgetManager>> sManagerCache = new WeakHashMap(); + static IGadgetService sService; + + Context mContext; + + public static GadgetManager getInstance(Context context) { + synchronized (sManagerCache) { + if (sService == null) { + IBinder b = ServiceManager.getService(Context.GADGET_SERVICE); + sService = IGadgetService.Stub.asInterface(b); + } + + WeakReference<GadgetManager> ref = sManagerCache.get(context); + GadgetManager result = null; + if (ref != null) { + result = ref.get(); + } + if (result == null) { + result = new GadgetManager(context); + sManagerCache.put(context, new WeakReference(result)); + } + return result; + } + } + + private GadgetManager(Context context) { + mContext = context; + } + + /** + * Call this with the new RemoteViews for your gadget whenever you need to. + * + * <p> + * This method will only work when called from the uid that owns the gadget provider. + * + * @param gadgetId The gadget instance for which to set the RemoteViews. + * @param views The RemoteViews object to show. + */ + public void updateGadget(int gadgetId, RemoteViews views) { + } + + /** + * Return a list of the gadgets that are currently installed. + */ + public List<GadgetInfo> getAvailableGadgets() { + return null; + } + + /** + * Get the available info about the gadget. If the gadgetId has not been bound yet, + * this method will return null. + * + * TODO: throws GadgetNotFoundException ??? if not valid + */ + public GadgetInfo getGadgetInfo(int gadgetId) { + try { + return sService.getGadgetInfo(gadgetId); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + + /** + * Get a gadgetId for a host in the calling process. + * + * @return a gadgetId + */ + public int allocateGadgetId(String hostPackage) { + try { + return sService.allocateGadgetId(hostPackage); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + + /** + * Delete the gadgetId. Same as removeGadget on GadgetHost. + */ + public void deleteGadgetId(int gadgetId) { + try { + sService.deleteGadgetId(gadgetId); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + + /** + * Set the component for a given gadgetId. You need the GADGET_LIST permission. + */ + public void bindGadgetId(int gadgetId, ComponentName provider) { + try { + sService.bindGadgetId(gadgetId, provider); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } +} + diff --git a/core/java/android/gadget/package.html b/core/java/android/gadget/package.html new file mode 100644 index 0000000..280ccfb --- /dev/null +++ b/core/java/android/gadget/package.html @@ -0,0 +1,4 @@ +<body> +@hide +</body> + diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index e2d7097..c09567c 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -55,6 +55,7 @@ public class Camera { private PreviewCallback mPreviewCallback; private AutoFocusCallback mAutoFocusCallback; private ErrorCallback mErrorCallback; + private boolean mOneShot; /** * Returns a new Camera object. @@ -198,9 +199,25 @@ public class Camera { */ public final void setPreviewCallback(PreviewCallback cb) { mPreviewCallback = cb; - setHasPreviewCallback(cb != null); + mOneShot = false; + setHasPreviewCallback(cb != null, false); } - private native final void setHasPreviewCallback(boolean installed); + + /** + * Installs a callback to retrieve a single preview frame, after which the + * callback is cleared. + * + * @param cb A callback object that receives a copy of the preview frame. + */ + public final void setOneShotPreviewCallback(PreviewCallback cb) { + if (cb != null) { + mPreviewCallback = cb; + mOneShot = true; + setHasPreviewCallback(true, true); + } + } + + private native final void setHasPreviewCallback(boolean installed, boolean oneshot); private class EventHandler extends Handler { @@ -230,8 +247,12 @@ public class Camera { return; case PREVIEW_CALLBACK: - if (mPreviewCallback != null) + if (mPreviewCallback != null) { mPreviewCallback.onPreviewFrame((byte[])msg.obj, mCamera); + if (mOneShot) { + mPreviewCallback = null; + } + } return; case AUTOFOCUS_CALLBACK: diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 21bb38e..1a7547d 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -33,6 +33,8 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; +import android.view.Window; +import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; @@ -197,6 +199,7 @@ public class InputMethodService extends AbstractInputMethodService { EditorInfo mInputEditorInfo; boolean mShowInputRequested; + boolean mLastShowInputRequested; boolean mShowCandidatesRequested; boolean mFullscreenApplied; @@ -257,6 +260,8 @@ public class InputMethodService extends AbstractInputMethodService { public void bindInput(InputBinding binding) { mInputBinding = binding; mInputConnection = binding.getConnection(); + if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding + + " ic=" + mInputConnection); onBindInput(); } @@ -264,17 +269,22 @@ public class InputMethodService extends AbstractInputMethodService { * Clear the current input binding. */ public void unbindInput() { + if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding + + " ic=" + mInputConnection); + onUnbindInput(); mInputStarted = false; mInputBinding = null; mInputConnection = null; } public void startInput(EditorInfo attribute) { + if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute); doStartInput(attribute, false); } public void restartInput(EditorInfo attribute) { - doStartInput(attribute, false); + if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute); + doStartInput(attribute, true); } /** @@ -305,6 +315,7 @@ public class InputMethodService extends AbstractInputMethodService { if (!isEnabled()) { return; } + if (DEBUG) Log.v(TAG, "finishInput() in " + this); onFinishInput(); mInputStarted = false; } @@ -455,7 +466,7 @@ public class InputMethodService extends AbstractInputMethodService { mIsInputViewShown = false; mExtractFrame.setVisibility(View.GONE); - mCandidatesFrame.setVisibility(View.GONE); + mCandidatesFrame.setVisibility(View.INVISIBLE); mInputFrame.setVisibility(View.GONE); } @@ -469,6 +480,29 @@ public class InputMethodService extends AbstractInputMethodService { } /** + * Take care of handling configuration changes. Subclasses of + * InputMethodService generally don't need to deal directly with + * this on their own; the standard implementation here takes care of + * regenerating the input method UI as a result of the configuration + * change, so you can rely on your {@link #onCreateInputView} and + * other methods being called as appropriate due to a configuration change. + */ + @Override public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + boolean visible = mWindowVisible; + boolean showingInput = mShowInputRequested; + boolean showingCandidates = mShowCandidatesRequested; + initViews(); + if (visible) { + if (showingCandidates) { + setCandidatesViewShown(true); + } + showWindow(showingInput); + } + } + + /** * Implement to return our standard {@link InputMethodImpl}. Subclasses * can override to provide their own customized version. */ @@ -493,6 +527,27 @@ public class InputMethodService extends AbstractInputMethodService { } /** + * Return the maximum width, in pixels, available the input method. + * Input methods are positioned at the bottom of the screen and, unless + * running in fullscreen, will generally want to be as short as possible + * so should compute their height based on their contents. However, they + * can stretch as much as needed horizontally. The function returns to + * you the maximum amount of space available horizontally, which you can + * use if needed for UI placement. + * + * <p>In many cases this is not needed, you can just rely on the normal + * view layout mechanisms to position your views within the full horizontal + * space given to the input method. + * + * <p>Note that this value can change dynamically, in particular when the + * screen orientation changes. + */ + public int getMaxWidth() { + WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); + return wm.getDefaultDisplay().getWidth(); + } + + /** * Return the currently active InputBinding for the input method, or * null if there is none. */ @@ -525,12 +580,19 @@ public class InputMethodService extends AbstractInputMethodService { * is currently running in fullscreen mode. */ public void updateFullscreenMode() { - boolean isFullscreen = onEvaluateFullscreenMode(); + boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode(); + boolean changed = mLastShowInputRequested != mShowInputRequested; if (mIsFullscreen != isFullscreen || !mFullscreenApplied) { + changed = true; mIsFullscreen = isFullscreen; mFullscreenApplied = true; - mWindow.getWindow().setBackgroundDrawable( - onCreateBackgroundDrawable()); + Drawable bg = onCreateBackgroundDrawable(); + if (bg == null) { + // We need to give the window a real drawable, so that it + // correctly sets its mode. + bg = getResources().getDrawable(android.R.color.transparent); + } + mWindow.getWindow().setBackgroundDrawable(bg); mExtractFrame.setVisibility(isFullscreen ? View.VISIBLE : View.GONE); if (isFullscreen) { if (mExtractView == null) { @@ -540,11 +602,39 @@ public class InputMethodService extends AbstractInputMethodService { } } startExtractingText(); - mWindow.getWindow().setLayout(FILL_PARENT, FILL_PARENT); - } else { - mWindow.getWindow().setLayout(WRAP_CONTENT, WRAP_CONTENT); } } + + if (changed) { + onConfigureWindow(mWindow.getWindow(), isFullscreen, + !mShowInputRequested); + mLastShowInputRequested = mShowInputRequested; + } + } + + /** + * Update the given window's parameters for the given mode. This is called + * when the window is first displayed and each time the fullscreen or + * candidates only mode changes. + * + * <p>The default implementation makes the layout for the window + * FILL_PARENT x FILL_PARENT when in fullscreen mode, and + * FILL_PARENT x WRAP_CONTENT when in non-fullscreen mode. + * + * @param win The input method's window. + * @param isFullscreen If true, the window is running in fullscreen mode + * and intended to cover the entire application display. + * @param isCandidatesOnly If true, the window is only showing the + * candidates view and none of the rest of its UI. This is mutually + * exclusive with fullscreen mode. + */ + public void onConfigureWindow(Window win, boolean isFullscreen, + boolean isCandidatesOnly) { + if (isFullscreen) { + mWindow.getWindow().setLayout(FILL_PARENT, FILL_PARENT); + } else { + mWindow.getWindow().setLayout(FILL_PARENT, WRAP_CONTENT); + } } /** @@ -607,7 +697,7 @@ public class InputMethodService extends AbstractInputMethodService { * is currently shown. */ public void updateInputViewShown() { - boolean isShown = onEvaluateInputViewShown(); + boolean isShown = mShowInputRequested && onEvaluateInputViewShown(); if (mIsInputViewShown != isShown && mWindowVisible) { mIsInputViewShown = isShown; mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE); @@ -650,18 +740,18 @@ public class InputMethodService extends AbstractInputMethodService { public void setCandidatesViewShown(boolean shown) { if (mShowCandidatesRequested != shown) { mCandidatesFrame.setVisibility(shown ? View.VISIBLE : View.INVISIBLE); - if (!mShowInputRequested) { - // If we are being asked to show the candidates view while the app - // has not asked for the input view to be shown, then we need - // to update whether the window is shown. - if (shown) { - showWindow(false); - } else { - hideWindow(); - } - } mShowCandidatesRequested = shown; } + if (!mShowInputRequested && mWindowVisible != shown) { + // If we are being asked to show the candidates view while the app + // has not asked for the input view to be shown, then we need + // to update whether the window is shown. + if (shown) { + showWindow(false); + } else { + hideWindow(); + } + } } public void setStatusIcon(int iconResId) { @@ -729,7 +819,7 @@ public class InputMethodService extends AbstractInputMethodService { * Called by the framework to create a Drawable for the background of * the input method window. May return null for no background. The default * implementation returns a non-null standard background only when in - * fullscreen mode. + * fullscreen mode. This is called each time the fullscreen mode changes. */ public Drawable onCreateBackgroundDrawable() { if (isFullscreenMode()) { @@ -789,22 +879,6 @@ public class InputMethodService extends AbstractInputMethodService { public void onStartInputView(EditorInfo info, boolean restarting) { } - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - - boolean visible = mWindowVisible; - boolean showingInput = mShowInputRequested; - boolean showingCandidates = mShowCandidatesRequested; - initViews(); - if (visible) { - if (showingCandidates) { - setCandidatesViewShown(true); - } - showWindow(showingInput); - } - } - /** * The system has decided that it may be time to show your input method. * This is called due to a corresponding call to your @@ -837,17 +911,17 @@ public class InputMethodService extends AbstractInputMethodService { boolean wasVisible = mWindowVisible; mWindowVisible = true; if (!mShowInputRequested) { - doShowInput = true; - mShowInputRequested = true; + if (showInput) { + doShowInput = true; + mShowInputRequested = true; + } } else { showInput = true; } - if (doShowInput) { - if (DEBUG) Log.v(TAG, "showWindow: updating UI"); - updateFullscreenMode(); - updateInputViewShown(); - } + if (DEBUG) Log.v(TAG, "showWindow: updating UI"); + updateFullscreenMode(); + updateInputViewShown(); if (!mWindowAdded || !mWindowCreated) { mWindowAdded = true; @@ -885,13 +959,44 @@ public class InputMethodService extends AbstractInputMethodService { } } + /** + * Called when a new client has bound to the input method. This + * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)} + * and {@link #onFinishInput()} calls as the user navigates through its + * UI. Upon this call you know that {@link #getCurrentInputBinding} + * and {@link #getCurrentInputConnection} return valid objects. + */ public void onBindInput() { } + /** + * Called when the previous bound client is no longer associated + * with the input method. After returning {@link #getCurrentInputBinding} + * and {@link #getCurrentInputConnection} will no longer return + * valid objects. + */ + public void onUnbindInput() { + } + + /** + * Called to inform the input method that text input has started in an + * editor. You should use this callback to initialize the state of your + * input to match the state of the editor given to it. + * + * @param attribute The attributes of the editor that input is starting + * in. + * @param restarting Set to true if input is restarting in the same + * editor such as because the application has changed the text in + * the editor. Otherwise will be false, indicating this is a new + * session with the editor. + */ public void onStartInput(EditorInfo attribute, boolean restarting) { } void doStartInput(EditorInfo attribute, boolean restarting) { + if (mInputStarted && !restarting) { + onFinishInput(); + } mInputStarted = true; mInputEditorInfo = attribute; onStartInput(attribute, restarting); @@ -903,6 +1008,13 @@ public class InputMethodService extends AbstractInputMethodService { } } + /** + * Called to inform the input method that text input has finished in + * the last editor. At this point there may be a call to + * {@link #onStartInput(EditorInfo, boolean)} to perform input in a + * new editor, or the input method may be left idle. This method is + * <em>not</em> called when input restarts in the same editor. + */ public void onFinishInput() { } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 7b64405..a18f37c 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1740,12 +1740,12 @@ public final class Settings { */ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/secure"); - + /** * Whether ADB is enabled. */ public static final String ADB_ENABLED = "adb_enabled"; - + /** * Setting to allow mock locations and location provider status to be injected into the * LocationManager service for testing purposes during application development. These @@ -1753,7 +1753,7 @@ public final class Settings { * by network, gps, or other location providers. */ public static final String ALLOW_MOCK_LOCATION = "mock_location"; - + /** * The Android ID (a unique 64-bit value) as a hex string. * Identical to that obtained by calling @@ -1761,24 +1761,40 @@ public final class Settings { * so you can get it without binding to a service. */ public static final String ANDROID_ID = "android_id"; - + /** * Whether bluetooth is enabled/disabled * 0=disabled. 1=enabled. */ public static final String BLUETOOTH_ON = "bluetooth_on"; - + + /** + * Get the key that retrieves a bluetooth headset's priority. + * @hide + */ + public static final String getBluetoothHeadsetPriorityKey(String address) { + return ("bluetooth_headset_priority_" + address.toUpperCase()); + } + + /** + * Get the key that retrieves a bluetooth a2dp sink's priority. + * @hide + */ + public static final String getBluetoothA2dpSinkPriorityKey(String address) { + return ("bluetooth_a2dp_sink_priority_" + address.toUpperCase()); + } + /** * Whether or not data roaming is enabled. (0 = false, 1 = true) */ public static final String DATA_ROAMING = "data_roaming"; - + /** * Setting to record the input method used by default, holding the ID * of the desired method. */ public static final String DEFAULT_INPUT_METHOD = "default_input_method"; - + /** * Whether the device has been provisioned (0 = false, 1 = true) */ @@ -1953,7 +1969,13 @@ public final class Settings { * Whether the Wi-Fi watchdog is enabled. */ public static final String WIFI_WATCHDOG_ON = "wifi_watchdog_on"; - + + /** + * A comma-separated list of SSIDs for which the Wi-Fi watchdog should be enabled. + * @hide pending API council + */ + public static final String WIFI_WATCHDOG_WATCH_LIST = "wifi_watchdog_watch_list"; + /** * The number of pings to test if an access point is a good connection. */ diff --git a/core/java/android/provider/UserDictionary.java b/core/java/android/provider/UserDictionary.java new file mode 100644 index 0000000..58e5731 --- /dev/null +++ b/core/java/android/provider/UserDictionary.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2008 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 android.provider; + +import java.util.Locale; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.net.Uri; +import android.text.TextUtils; + +/** + * + * @hide Pending API council approval + */ +public class UserDictionary { + public static final String AUTHORITY = "user_dictionary"; + + /** + * The content:// style URL for this provider + */ + public static final Uri CONTENT_URI = + Uri.parse("content://" + AUTHORITY); + + /** + * Contains the user defined words. + * @hide Pending API council approval + */ + public static class Words implements BaseColumns { + /** + * The content:// style URL for this table + */ + public static final Uri CONTENT_URI = + Uri.parse("content://" + AUTHORITY + "/words"); + + /** + * The MIME type of {@link #CONTENT_URI} providing a directory of words. + */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.google.userword"; + + /** + * The MIME type of a {@link #CONTENT_URI} sub-directory of a single word. + */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.google.userword"; + + public static final String _ID = BaseColumns._ID; + + /** + * The word column. + * <p>TYPE: TEXT</p> + */ + public static final String WORD = "word"; + + /** + * The frequency column. A value between 1 and 255. + * <p>TYPE: INTEGER</p> + */ + public static final String FREQUENCY = "frequency"; + + /** + * The locale that this word belongs to. Null if it pertains to all + * locales. Locale is a 5 letter string such as <pre>en_US</pre>. + * <p>TYPE: TEXT</p> + */ + public static final String LOCALE = "locale"; + + /** + * The uid of the application that inserted the word. + * <p>TYPE: INTEGER</p> + */ + public static final String APP_ID = "appid"; + + public static final int LOCALE_TYPE_ALL = 0; + + public static final int LOCALE_TYPE_CURRENT = 1; + + /** + * Sort by descending order of frequency. + */ + public static final String DEFAULT_SORT_ORDER = FREQUENCY + " DESC"; + + + public static void addWord(Context context, String word, + int frequency, int localeType) { + final ContentResolver resolver = context.getContentResolver(); + + if (TextUtils.isEmpty(word) || localeType < 0 || localeType > 1) { + return; + } + + if (frequency < 0) frequency = 0; + if (frequency > 255) frequency = 255; + + String locale = null; + + // TODO: Verify if this is the best way to get the current locale + if (localeType == LOCALE_TYPE_CURRENT) { + locale = Locale.getDefault().toString(); + } + ContentValues values = new ContentValues(4); + + values.put(WORD, word); + values.put(FREQUENCY, frequency); + values.put(LOCALE, locale); + values.put(APP_ID, 0); // TODO: Get App UID + + Uri result = resolver.insert(CONTENT_URI, values); + // It's ok if the insert doesn't succeed because the word + // already exists. + } + } +} diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java index 90ef8f6..be784ff 100644 --- a/core/java/android/server/BluetoothA2dpService.java +++ b/core/java/android/server/BluetoothA2dpService.java @@ -35,6 +35,7 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.media.AudioManager; import android.os.Binder; +import android.provider.Settings; import android.util.Log; import java.io.FileDescriptor; @@ -83,6 +84,8 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { mIntentFilter = new IntentFilter(BluetoothIntent.ENABLED_ACTION); mIntentFilter.addAction(BluetoothIntent.DISABLED_ACTION); + mIntentFilter.addAction(BluetoothIntent.BONDING_CREATED_ACTION); + mIntentFilter.addAction(BluetoothIntent.BONDING_REMOVED_ACTION); mContext.registerReceiver(mReceiver, mIntentFilter); if (device.isEnabled()) { @@ -103,10 +106,15 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); + String address = intent.getStringExtra(BluetoothIntent.ADDRESS); if (action.equals(BluetoothIntent.ENABLED_ACTION)) { onBluetoothEnable(); } else if (action.equals(BluetoothIntent.DISABLED_ACTION)) { onBluetoothDisable(); + } else if (action.equals(BluetoothIntent.BONDING_CREATED_ACTION)) { + setSinkPriority(address, BluetoothA2dp.PRIORITY_AUTO); + } else if (action.equals(BluetoothIntent.BONDING_REMOVED_ACTION)) { + setSinkPriority(address, BluetoothA2dp.PRIORITY_OFF); } } }; @@ -145,7 +153,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { if (path == null) { return BluetoothError.ERROR; } - + SinkState sink = mAudioDevices.get(path); int state = BluetoothA2dp.STATE_DISCONNECTED; if (sink != null) { @@ -159,8 +167,8 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { case BluetoothA2dp.STATE_CONNECTING: return BluetoothError.SUCCESS; } - - // State is DISCONNECTED + + // State is DISCONNECTED if (!connectSinkNative(path)) { return BluetoothError.ERROR; } @@ -189,7 +197,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { return BluetoothError.SUCCESS; } - // State is CONNECTING or CONNECTED or PLAYING + // State is CONNECTING or CONNECTED or PLAYING if (!disconnectSinkNative(path)) { return BluetoothError.ERROR; } else { @@ -229,16 +237,37 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { return BluetoothA2dp.STATE_DISCONNECTED; } - public synchronized void onHeadsetCreated(String path) { + public synchronized int getSinkPriority(String address) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (!BluetoothDevice.checkBluetoothAddress(address)) { + return BluetoothError.ERROR; + } + return Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.getBluetoothA2dpSinkPriorityKey(address), + BluetoothA2dp.PRIORITY_OFF); + } + + public synchronized int setSinkPriority(String address, int priority) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + if (!BluetoothDevice.checkBluetoothAddress(address)) { + return BluetoothError.ERROR; + } + return Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.getBluetoothA2dpSinkPriorityKey(address), priority) ? + BluetoothError.SUCCESS : BluetoothError.ERROR; + } + + private synchronized void onHeadsetCreated(String path) { updateState(path, BluetoothA2dp.STATE_DISCONNECTED); } - public synchronized void onHeadsetRemoved(String path) { + private synchronized void onHeadsetRemoved(String path) { if (mAudioDevices == null) return; mAudioDevices.remove(path); } - public synchronized void onSinkConnected(String path) { + private synchronized void onSinkConnected(String path) { if (mAudioDevices == null) return; // bluez 3.36 quietly disconnects the previous sink when a new sink // is connected, so we need to mark all previously connected sinks as @@ -258,16 +287,16 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { updateState(path, BluetoothA2dp.STATE_CONNECTED); } - public synchronized void onSinkDisconnected(String path) { + private synchronized void onSinkDisconnected(String path) { mAudioManager.setBluetoothA2dpOn(false); updateState(path, BluetoothA2dp.STATE_DISCONNECTED); } - public synchronized void onSinkPlaying(String path) { + private synchronized void onSinkPlaying(String path) { updateState(path, BluetoothA2dp.STATE_PLAYING); } - public synchronized void onSinkStopped(String path) { + private synchronized void onSinkStopped(String path) { updateState(path, BluetoothA2dp.STATE_CONNECTED); } @@ -307,7 +336,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { if (state != prevState) { if (DBG) log("state " + address + " (" + path + ") " + prevState + "->" + state); - + Intent intent = new Intent(BluetoothA2dp.SINK_STATE_CHANGED_ACTION); intent.putExtra(BluetoothIntent.ADDRESS, address); intent.putExtra(BluetoothA2dp.SINK_PREVIOUS_STATE, prevState); @@ -339,5 +368,4 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private synchronized native boolean connectSinkNative(String path); private synchronized native boolean disconnectSinkNative(String path); private synchronized native boolean isSinkConnectedNative(String path); - } diff --git a/core/java/android/speech/srec/Recognizer.java b/core/java/android/speech/srec/Recognizer.java index 749c923..a03a36a 100644 --- a/core/java/android/speech/srec/Recognizer.java +++ b/core/java/android/speech/srec/Recognizer.java @@ -367,6 +367,35 @@ public final class Recognizer { SR_RecognizerStop(mRecognizer); SR_RecognizerDeactivateRule(mRecognizer, mActiveGrammar.mGrammar, "trash"); } + + /** + * Reset the acoustic state vectorto it's default value. + * + * @hide + */ + public void resetAcousticState() { + SR_AcousticStateReset(mRecognizer); + } + + /** + * Set the acoustic state vector. + * @param state String containing the acoustic state vector. + * + * @hide + */ + public void setAcousticState(String state) { + SR_AcousticStateSet(mRecognizer, state); + } + + /** + * Get the acoustic state vector. + * @return String containing the acoustic state vector. + * + * @hide + */ + public String getAcousticState() { + return SR_AcousticStateGet(mRecognizer); + } /** * Clean up resources. @@ -572,6 +601,9 @@ public final class Recognizer { return "EVENT_" + event; } + // + // SR_Recognizer methods + // private static native void SR_RecognizerStart(int recognizer); private static native void SR_RecognizerStop(int recognizer); private static native int SR_RecognizerCreate(); @@ -615,6 +647,14 @@ public final class Recognizer { private static native boolean SR_RecognizerIsSignalTooFewSamples(int recognizer); private static native boolean SR_RecognizerIsSignalTooManySamples(int recognizer); // private static native void SR_Recognizer_Change_Sample_Rate (size_t new_sample_rate); + + + // + // SR_AcousticState native methods + // + private static native void SR_AcousticStateReset(int recognizer); + private static native void SR_AcousticStateSet(int recognizer, String state); + private static native String SR_AcousticStateGet(int recognizer); // diff --git a/core/java/android/speech/srec/WaveHeader.java b/core/java/android/speech/srec/WaveHeader.java index 0aa3cc2..a99496d 100644 --- a/core/java/android/speech/srec/WaveHeader.java +++ b/core/java/android/speech/srec/WaveHeader.java @@ -263,5 +263,12 @@ public class WaveHeader { out.write(val >> 0); out.write(val >> 8); } + + @Override + public String toString() { + return String.format( + "WaveHeader format=%d numChannels=%d sampleRate=%d bitsPerSample=%d numBytes=%d", + mFormat, mNumChannels, mSampleRate, mBitsPerSample, mNumBytes); + } } diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 9d7a124..9e0289a 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -117,6 +117,7 @@ public final class ViewRoot extends Handler implements ViewParent, View mView; View mFocusedView; + View mRealFocusedView; // this is not set to null in touch mode int mViewVisibility; boolean mAppVisible = true; @@ -971,9 +972,19 @@ public final class ViewRoot extends Handler implements ViewParent, if (mFirst) { // handle first focus request - if (mView != null && !mView.hasFocus()) { - mView.requestFocus(View.FOCUS_FORWARD); - mFocusedView = mView.findFocus(); + if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()=" + + mView.hasFocus()); + if (mView != null) { + if (!mView.hasFocus()) { + mView.requestFocus(View.FOCUS_FORWARD); + mFocusedView = mRealFocusedView = mView.findFocus(); + if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view=" + + mFocusedView); + } else { + mRealFocusedView = mView.findFocus(); + if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view=" + + mRealFocusedView); + } } } @@ -1214,13 +1225,16 @@ public final class ViewRoot extends Handler implements ViewParent, // requestChildRectangleOnScreen() call (in which case 'rectangle' // is non-null and we just want to scroll to whatever that // rectangle is). - View focus = mFocusedView; + View focus = mRealFocusedView; if (focus != mLastScrolledFocus) { // If the focus has changed, then ignore any requests to scroll // to a rectangle; first we want to make sure the entire focus // view is visible. rectangle = null; } + if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Eval scroll: focus=" + focus + + " rectangle=" + rectangle + " ci=" + ci + + " vi=" + vi); if (focus == mLastScrolledFocus && !mScrollMayChange && rectangle == null) { // Optimization: if the focus hasn't changed since last @@ -1234,6 +1248,7 @@ public final class ViewRoot extends Handler implements ViewParent, // a pan so it can be seen. mLastScrolledFocus = focus; mScrollMayChange = false; + if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Need to scroll?"); // Try to find the rectangle from the focus view. if (focus.getGlobalVisibleRect(mVisRect, null)) { if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Root w=" @@ -1307,7 +1322,9 @@ public final class ViewRoot extends Handler implements ViewParent, mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused); scheduleTraversals(); } - mFocusedView = focused; + mFocusedView = mRealFocusedView = focused; + if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now " + + mFocusedView); } public void clearChildFocus(View child) { @@ -1315,7 +1332,8 @@ public final class ViewRoot extends Handler implements ViewParent, View oldFocus = mFocusedView; - mFocusedView = null; + if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Clearing child focus"); + mFocusedView = mRealFocusedView = null; if (mView != null && !mView.hasFocus()) { // If a view gets the focus, the listener will be invoked from requestChildFocus() if (!mView.requestFocus(View.FOCUS_FORWARD)) { diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index a676234..ba40782 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -800,7 +800,7 @@ public final class InputMethodManager { try { if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic=" - + ic + " tba=" + tba); + + ic + " tba=" + tba + " initial=" + initial); InputBindResult res = mService.startInput(mClient, tba, initial, mCurMethod == null); if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); @@ -848,6 +848,9 @@ public final class InputMethodManager { synchronized (mH) { if (DEBUG) Log.v(TAG, "focusIn: " + view); // Okay we have a new view that is being served. + if (mServedView != view) { + mCurrentTextBoxAttribute = null; + } mServedView = view; mCompletions = null; mServedConnecting = true; diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java index cae94c9..4f8e5e4 100644 --- a/core/java/android/webkit/CallbackProxy.java +++ b/core/java/android/webkit/CallbackProxy.java @@ -16,8 +16,10 @@ package android.webkit; +import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.net.Uri; @@ -30,7 +32,14 @@ import android.os.SystemClock; import android.util.Config; import android.util.Log; import android.view.KeyEvent; - +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; +import android.widget.TextView; +import com.android.internal.R; + +import java.net.MalformedURLException; +import java.net.URL; import java.util.HashMap; /** @@ -376,12 +385,24 @@ class CallbackProxy extends Handler { case JS_ALERT: if (mWebChromeClient != null) { - JsResult res = (JsResult) msg.obj; + final JsResult res = (JsResult) msg.obj; String message = msg.getData().getString("message"); String url = msg.getData().getString("url"); if (!mWebChromeClient.onJsAlert(mWebView, url, message, - res)) { - res.handleDefault(); + res)) { + new AlertDialog.Builder(mContext) + .setTitle(getJsDialogTitle(url)) + .setMessage(message) + .setPositiveButton(R.string.ok, + new AlertDialog.OnClickListener() { + public void onClick( + DialogInterface dialog, + int which) { + res.confirm(); + } + }) + .setCancelable(false) + .show(); } res.setReady(); } @@ -389,12 +410,29 @@ class CallbackProxy extends Handler { case JS_CONFIRM: if (mWebChromeClient != null) { - JsResult res = (JsResult) msg.obj; + final JsResult res = (JsResult) msg.obj; String message = msg.getData().getString("message"); String url = msg.getData().getString("url"); if (!mWebChromeClient.onJsConfirm(mWebView, url, message, - res)) { - res.handleDefault(); + res)) { + new AlertDialog.Builder(mContext) + .setTitle(getJsDialogTitle(url)) + .setMessage(message) + .setPositiveButton(R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick( + DialogInterface dialog, + int which) { + res.confirm(); + }}) + .setNegativeButton(R.string.cancel, + new DialogInterface.OnClickListener() { + public void onClick( + DialogInterface dialog, + int which) { + res.cancel(); + }}) + .show(); } // Tell the JsResult that it is ready for client // interaction. @@ -404,13 +442,49 @@ class CallbackProxy extends Handler { case JS_PROMPT: if (mWebChromeClient != null) { - JsPromptResult res = (JsPromptResult) msg.obj; + final JsPromptResult res = (JsPromptResult) msg.obj; String message = msg.getData().getString("message"); String defaultVal = msg.getData().getString("default"); String url = msg.getData().getString("url"); if (!mWebChromeClient.onJsPrompt(mWebView, url, message, defaultVal, res)) { - res.handleDefault(); + final LayoutInflater factory = LayoutInflater + .from(mContext); + final View view = factory.inflate(R.layout.js_prompt, + null); + final EditText v = (EditText) view + .findViewById(R.id.value); + v.setText(defaultVal); + ((TextView) view.findViewById(R.id.message)) + .setText(message); + new AlertDialog.Builder(mContext) + .setTitle(getJsDialogTitle(url)) + .setView(view) + .setPositiveButton(R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick( + DialogInterface dialog, + int whichButton) { + res.confirm(v.getText() + .toString()); + } + }) + .setNegativeButton(R.string.cancel, + new DialogInterface.OnClickListener() { + public void onClick( + DialogInterface dialog, + int whichButton) { + res.cancel(); + } + }) + .setOnCancelListener( + new DialogInterface.OnCancelListener() { + public void onCancel( + DialogInterface dialog) { + res.cancel(); + } + }) + .show(); } // Tell the JsResult that it is ready for client // interaction. @@ -420,12 +494,32 @@ class CallbackProxy extends Handler { case JS_UNLOAD: if (mWebChromeClient != null) { - JsResult res = (JsResult) msg.obj; + final JsResult res = (JsResult) msg.obj; String message = msg.getData().getString("message"); String url = msg.getData().getString("url"); if (!mWebChromeClient.onJsBeforeUnload(mWebView, url, - message, res)) { - res.handleDefault(); + message, res)) { + final String m = mContext.getString( + R.string.js_dialog_before_unload, message); + new AlertDialog.Builder(mContext) + .setMessage(m) + .setPositiveButton(R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick( + DialogInterface dialog, + int which) { + res.confirm(); + } + }) + .setNegativeButton(R.string.cancel, + new DialogInterface.OnClickListener() { + public void onClick( + DialogInterface dialog, + int which) { + res.cancel(); + } + }) + .show(); } res.setReady(); } @@ -468,6 +562,24 @@ class CallbackProxy extends Handler { sendMessage(obtainMessage(SWITCH_OUT_HISTORY)); } + private String getJsDialogTitle(String url) { + String title = url; + if (URLUtil.isDataUrl(url)) { + // For data: urls, we just display 'JavaScript' similar to Safari. + title = mContext.getString(R.string.js_dialog_title_default); + } else { + try { + URL aUrl = new URL(url); + // For example: "The page at 'http://www.mit.edu' says:" + title = mContext.getString(R.string.js_dialog_title, + aUrl.getProtocol() + "://" + aUrl.getHost()); + } catch (MalformedURLException ex) { + // do nothing. just use the url as the title + } + } + return title; + } + //-------------------------------------------------------------------------- // WebViewClient functions. // NOTE: shouldOverrideKeyEvent is never called from the WebCore thread so diff --git a/core/java/android/webkit/TextDialog.java b/core/java/android/webkit/TextDialog.java index 30b519a..b7b40b1 100644 --- a/core/java/android/webkit/TextDialog.java +++ b/core/java/android/webkit/TextDialog.java @@ -38,13 +38,20 @@ import android.text.method.MovementMethod; import android.text.method.PasswordTransformationMethod; import android.text.method.TextKeyListener; import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; import android.view.View.MeasureSpec; import android.view.ViewConfiguration; import android.widget.AbsoluteLayout.LayoutParams; +import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; +import android.widget.TextView; + +import java.util.ArrayList; /** * TextDialog is a specialized version of EditText used by WebView @@ -281,7 +288,7 @@ import android.widget.AutoCompleteTextView; } return false; } - + /** * Determine whether this TextDialog currently represents the node * represented by ptr. @@ -406,6 +413,9 @@ import android.widget.AutoCompleteTextView; * focus to the host. */ /* package */ void remove() { + // hide the soft keyboard when the edit text is out of focus + InputMethodManager.getInstance(mContext).hideSoftInputFromWindow( + getWindowToken(), 0); mHandler.removeMessages(LONGPRESS); mWebView.removeView(this); mWebView.requestFocus(); @@ -427,6 +437,43 @@ import android.widget.AutoCompleteTextView; mWebView.passToJavaScript(getText().toString(), event); } + public void setAdapterCustom(AutoCompleteAdapter adapter) { + adapter.setTextView(this); + super.setAdapter(adapter); + } + + /** + * This is a special version of ArrayAdapter which changes its text size + * to match the text size of its host TextView. + */ + public static class AutoCompleteAdapter extends ArrayAdapter<String> { + private TextView mTextView; + + public AutoCompleteAdapter(Context context, ArrayList<String> entries) { + super(context, com.android.internal.R.layout + .search_dropdown_item_1line, entries); + } + + /** + * {@inheritDoc} + */ + public View getView(int position, View convertView, ViewGroup parent) { + TextView tv = + (TextView) super.getView(position, convertView, parent); + if (tv != null && mTextView != null) { + tv.setTextSize(mTextView.getTextSize()); + } + return tv; + } + + /** + * Set the TextView so we can match its text size. + */ + private void setTextView(TextView tv) { + mTextView = tv; + } + } + /** * Determine whether to use the system-wide password disguising method, * or to use none. diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index f00238d..9cfc622 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -57,6 +57,7 @@ import android.view.ViewGroup; import android.view.ViewParent; import android.view.ViewTreeObserver; import android.view.inputmethod.InputMethodManager; +import android.webkit.TextDialog.AutoCompleteAdapter; import android.webkit.WebViewCore.EventHub; import android.widget.AbsoluteLayout; import android.widget.AdapterView; @@ -2819,10 +2820,8 @@ public class WebView extends AbsoluteLayout public void run() { ArrayList<String> pastEntries = mDatabase.getFormData(mUrl, mName); if (pastEntries.size() > 0) { - ArrayAdapter<String> adapter = new ArrayAdapter<String>( - mContext, com.android.internal.R.layout - .search_dropdown_item_1line, - pastEntries); + AutoCompleteAdapter adapter = new + AutoCompleteAdapter(mContext, pastEntries); ((HashMap) mUpdateMessage.obj).put("adapter", adapter); mUpdateMessage.sendToTarget(); } @@ -4458,9 +4457,9 @@ public class WebView extends AbsoluteLayout case UPDATE_TEXT_ENTRY_ADAPTER: HashMap data = (HashMap) msg.obj; if (mTextEntry.isSameTextField(msg.arg1)) { - ArrayAdapter<String> adapter = - (ArrayAdapter<String>) data.get("adapter"); - mTextEntry.setAdapter(adapter); + AutoCompleteAdapter adapter = + (AutoCompleteAdapter) data.get("adapter"); + mTextEntry.setAdapterCustom(adapter); } break; case UPDATE_CLIPBOARD: diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index 024b663..7d52901 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -413,7 +413,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe // when the selection is at the bottom, we block the // event to avoid going to the next focusable widget Adapter adapter = mDropDownList.getAdapter(); - if (curIndex == adapter.getCount() - 1) { + if (adapter != null && curIndex == adapter.getCount() - 1) { return true; } } diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java index 6c0c164..f2cec92 100644 --- a/core/java/android/widget/MediaController.java +++ b/core/java/android/widget/MediaController.java @@ -393,6 +393,12 @@ public class MediaController extends FrameLayout { doPauseResume(); show(sDefaultTimeout); return true; + } else if (keyCode == KeyEvent.KEYCODE_STOP) { + if (mPlayer.isPlaying()) { + mPlayer.pause(); + updatePausePlay(); + } + return true; } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { // don't show the controls for volume adjustment diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java index df40156..1227afd 100644 --- a/core/java/android/widget/VideoView.java +++ b/core/java/android/widget/VideoView.java @@ -447,7 +447,8 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { keyCode != KeyEvent.KEYCODE_ENDCALL && mMediaPlayer != null && mMediaController != null) { - if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK) { + if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || + keyCode == KeyEvent.KEYCODE_PLAYPAUSE) { if (mMediaPlayer.isPlaying()) { pause(); mMediaController.show(); @@ -456,6 +457,10 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { mMediaController.hide(); } return true; + } else if (keyCode == KeyEvent.KEYCODE_STOP + && mMediaPlayer.isPlaying()) { + pause(); + mMediaController.show(); } else { toggleMediaControlsVisiblity(); } |