diff options
Diffstat (limited to 'core/java/com/android')
20 files changed, 996 insertions, 1294 deletions
diff --git a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java new file mode 100644 index 0000000..000f6c4 --- /dev/null +++ b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2007 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.Handler; +import android.os.IMountService; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.Environment; +import android.widget.Toast; +import android.util.Log; + +/** + * This activity is shown to the user to confirm formatting of external media. + * It uses the alert dialog style. It will be launched from a notification, or from settings + */ +public class ExternalMediaFormatActivity extends AlertActivity implements DialogInterface.OnClickListener { + + private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1; + + /** Used to detect when the media state changes, in case we need to call finish() */ + private BroadcastReceiver mStorageReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + Log.d("ExternalMediaFormatActivity", "got action " + action); + + if (action == Intent.ACTION_MEDIA_REMOVED || + action == Intent.ACTION_MEDIA_CHECKING || + action == Intent.ACTION_MEDIA_MOUNTED || + action == Intent.ACTION_MEDIA_SHARED) { + finish(); + } + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Log.d("ExternalMediaFormatActivity", "onCreate!"); + // Set up the "dialog" + final AlertController.AlertParams p = mAlertParams; + p.mIconId = com.android.internal.R.drawable.stat_sys_warning; + p.mTitle = getString(com.android.internal.R.string.extmedia_format_title); + p.mMessage = getString(com.android.internal.R.string.extmedia_format_message); + p.mPositiveButtonText = getString(com.android.internal.R.string.extmedia_format_button_format); + p.mPositiveButtonListener = this; + p.mNegativeButtonText = getString(com.android.internal.R.string.cancel); + p.mNegativeButtonListener = this; + setupAlert(); + } + + @Override + protected void onResume() { + super.onResume(); + + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_MEDIA_REMOVED); + filter.addAction(Intent.ACTION_MEDIA_CHECKING); + filter.addAction(Intent.ACTION_MEDIA_MOUNTED); + filter.addAction(Intent.ACTION_MEDIA_SHARED); + registerReceiver(mStorageReceiver, filter); + } + + @Override + protected void onPause() { + super.onPause(); + + unregisterReceiver(mStorageReceiver); + } + + /** + * {@inheritDoc} + */ + public void onClick(DialogInterface dialog, int which) { + + if (which == POSITIVE_BUTTON) { + IMountService mountService = IMountService.Stub.asInterface(ServiceManager + .getService("mount")); + if (mountService != null) { + try { + mountService.formatMedia(Environment.getExternalStorageDirectory().toString()); + } catch (RemoteException e) { + } + } + } + + // No matter what, finish the activity + finish(); + } +} diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 434850c..eb232c7 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -22,8 +22,14 @@ interface IBatteryStats { BatteryStatsImpl getStatistics(); void noteStartWakelock(int uid, String name, int type); void noteStopWakelock(int uid, String name, int type); - void noteStartSensor(int uid, int sensor); - void noteStopSensor(int uid, int sensor); + void noteStartSensor(int uid, String name, int sensor); + void noteStopSensor(int uid, String name, int sensor); + void noteRequestGpsOn(int uid); + void noteRequestGpsOff(int uid); + void noteStartGps(int uid); + void noteStopGps(int uid); + void noteScreenOn(); + void noteScreenOff(); void setOnBattery(boolean onBattery); long getAwakeTimeBattery(); long getAwakeTimePlugged(); diff --git a/core/java/com/android/internal/app/IUsageStats.aidl b/core/java/com/android/internal/app/IUsageStats.aidl new file mode 100755 index 0000000..6b053d5 --- /dev/null +++ b/core/java/com/android/internal/app/IUsageStats.aidl @@ -0,0 +1,27 @@ +/* + * 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 com.android.internal.app; + +import android.content.ComponentName; +import com.android.internal.os.PkgUsageStats; + +interface IUsageStats { + void noteResumeComponent(in ComponentName componentName); + void notePauseComponent(in ComponentName componentName); + PkgUsageStats getPkgUsageStats(in ComponentName componentName); + PkgUsageStats[] getAllPkgUsageStats(); +} diff --git a/core/java/com/android/internal/app/UsbStorageStopActivity.java b/core/java/com/android/internal/app/UsbStorageStopActivity.java new file mode 100644 index 0000000..30523c4 --- /dev/null +++ b/core/java/com/android/internal/app/UsbStorageStopActivity.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2007 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.Handler; +import android.os.IMountService; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.widget.Toast; + +/** + * This activity is shown to the user for him/her to disable USB mass storage. + * It uses the alert dialog style. It will be launched from a notification. + */ +public class UsbStorageStopActivity extends AlertActivity implements DialogInterface.OnClickListener { + + private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1; + + /** Used to detect when the USB cable is unplugged, so we can call finish() */ + private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction() == Intent.ACTION_BATTERY_CHANGED) { + handleBatteryChanged(intent); + } + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Set up the "dialog" + final AlertController.AlertParams p = mAlertParams; + p.mIconId = com.android.internal.R.drawable.stat_sys_warning; + p.mTitle = getString(com.android.internal.R.string.usb_storage_stop_title); + p.mMessage = getString(com.android.internal.R.string.usb_storage_stop_message); + p.mPositiveButtonText = getString(com.android.internal.R.string.usb_storage_stop_button_mount); + p.mPositiveButtonListener = this; + p.mNegativeButtonText = getString(com.android.internal.R.string.usb_storage_stop_button_unmount); + p.mNegativeButtonListener = this; + setupAlert(); + } + + @Override + protected void onResume() { + super.onResume(); + + registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + } + + @Override + protected void onPause() { + super.onPause(); + + unregisterReceiver(mBatteryReceiver); + } + + /** + * {@inheritDoc} + */ + public void onClick(DialogInterface dialog, int which) { + + if (which == POSITIVE_BUTTON) { + stopUsbStorage(); + } + + // No matter what, finish the activity + finish(); + } + + private void stopUsbStorage() { + IMountService mountService = IMountService.Stub.asInterface(ServiceManager + .getService("mount")); + if (mountService == null) { + showStoppingError(); + return; + } + + try { + mountService.setMassStorageEnabled(false); + } catch (RemoteException e) { + showStoppingError(); + return; + } + } + + private void handleBatteryChanged(Intent intent) { + int pluggedType = intent.getIntExtra("plugged", 0); + if (pluggedType == 0) { + // It was disconnected from the plug, so finish + finish(); + } + } + + private void showStoppingError() { + Toast.makeText(this, com.android.internal.R.string.usb_storage_stop_error_message, + Toast.LENGTH_LONG).show(); + } + +} diff --git a/core/java/com/android/internal/gadget/IGadgetHost.aidl b/core/java/com/android/internal/gadget/IGadgetHost.aidl new file mode 100644 index 0000000..a5b8654 --- /dev/null +++ b/core/java/com/android/internal/gadget/IGadgetHost.aidl @@ -0,0 +1,27 @@ +/* + * 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 com.android.internal.gadget; + +import android.content.ComponentName; +import android.gadget.GadgetInfo; +import android.widget.RemoteViews; + +/** {@hide} */ +oneway interface IGadgetHost { + void updateGadget(int gadgetId, in RemoteViews views); +} + diff --git a/core/java/com/android/internal/gadget/IGadgetService.aidl b/core/java/com/android/internal/gadget/IGadgetService.aidl index 6f9af04..1b3946f 100644 --- a/core/java/com/android/internal/gadget/IGadgetService.aidl +++ b/core/java/com/android/internal/gadget/IGadgetService.aidl @@ -18,12 +18,32 @@ package com.android.internal.gadget; import android.content.ComponentName; import android.gadget.GadgetInfo; +import com.android.internal.gadget.IGadgetHost; +import android.widget.RemoteViews; /** {@hide} */ interface IGadgetService { - int allocateGadgetId(String hostPackage); + + // + // for GadgetHost + // + int[] startListening(IGadgetHost host, String packageName, int hostId, + out List<RemoteViews> updatedViews); + void stopListening(int hostId); + int allocateGadgetId(String packageName, int hostId); void deleteGadgetId(int gadgetId); - void bindGadgetId(int gadgetId, in ComponentName provider); - GadgetInfo getGadgetInfo(int gadgetId); + void deleteHost(int hostId); + void deleteAllHosts(); + RemoteViews getGadgetViews(int gadgetId); + + // + // for GadgetManager + // + void updateGadgetIds(in int[] gadgetIds, in RemoteViews views); + void updateGadgetProvider(in ComponentName provider, in RemoteViews views); List<GadgetInfo> getInstalledProviders(); + GadgetInfo getGadgetInfo(int gadgetId); + void bindGadgetId(int gadgetId, in ComponentName provider); + } + diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 8912960..cbb65dc 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -17,6 +17,7 @@ package com.android.internal.os; import android.os.BatteryStats; +import android.os.NetStat; import android.os.Parcel; import android.os.ParcelFormatException; import android.os.Parcelable; @@ -28,9 +29,9 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; /** @@ -39,12 +40,14 @@ import java.util.Map; * otherwise. */ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { + + private static final String TAG = "BatteryStatsImpl"; // In-memory Parcel magic number, used to detect attempts to unmarshall bad data private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 13; + private static final int VERSION = 15; private final File mFile; private final File mBackupFile; @@ -77,7 +80,11 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { long mRealtime; long mRealtimeStart; long mLastRealtime; - + + boolean mScreenOn; + long mLastScreenOnTimeMillis; + long mBatteryScreenOnTimeMillis; + long mPluggedScreenOnTimeMillis; /** * These provide time bases that discount the time the device is plugged * in to power. @@ -87,6 +94,11 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { long mTrackBatteryUptimeStart; long mTrackBatteryPastRealtime; long mTrackBatteryRealtimeStart; + + long mUnpluggedBatteryUptime; + long mUnpluggedBatteryRealtime; + + HashSet<Integer> mGpsRequesters = new HashSet<Integer>(); long mLastWriteTime = 0; // Milliseconds @@ -255,14 +267,13 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { long realtime = SystemClock.elapsedRealtime() * 1000; long heldTime = stats.getBatteryUptimeLocked(realtime) - mUpdateTime; if (heldTime > 0) { - mTotalTime += (heldTime * 1000) / mCount; + mTotalTime += heldTime / mCount; } mUpdateTime = stats.getBatteryUptimeLocked(realtime); } private long computeRunTimeLocked(long curBatteryUptime) { - return mTotalTime + - (mNesting > 0 ? ((curBatteryUptime * 1000) - mUpdateTime) / mCount : 0); + return mTotalTime + (mNesting > 0 ? (curBatteryUptime - mUpdateTime) / mCount : 0); } void writeSummaryFromParcelLocked(Parcel out, long curBatteryUptime) { @@ -284,6 +295,15 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } } + public void unplugTcpCounters() { + final int NU = mUidStats.size(); + for (int iu = 0; iu < NU; iu++) { + Uid u = mUidStats.valueAt(iu); + u.mTcpBytesReceivedAtLastUnplug = u.getTcpBytesReceived(STATS_TOTAL); + u.mTcpBytesSentAtLastUnplug = u.getTcpBytesSent(STATS_TOTAL); + } + } + public void unplugTimers() { ArrayList<Timer> timers; @@ -305,6 +325,69 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } } + public void noteStartGps(int uid) { + mGpsRequesters.add(uid); + mUidStats.get(uid).noteStartGps(); + } + + public void noteStopGps(int uid) { + mGpsRequesters.remove(uid); + mUidStats.get(uid).noteStopGps(); + } + + public void noteRequestGpsOn(int uid) { + mGpsRequesters.add(uid); + mUidStats.get(uid).noteStartGps(); + } + + public void noteRequestGpsOff(int uid) { + mGpsRequesters.remove(uid); + mUidStats.get(uid).noteStopGps(); + } + + /** + * When the device screen or battery state changes, update the appropriate "screen on time" + * counter. + */ + private void updateScreenOnTime(boolean screenOn) { + if (!mScreenOn) { + Log.w(TAG, "updateScreenOnTime without mScreenOn, ignored"); + return; + } + long now = SystemClock.elapsedRealtime(); + long elapsed = now - mLastScreenOnTimeMillis; + if (mOnBattery) { + mBatteryScreenOnTimeMillis += elapsed; + } else { + mPluggedScreenOnTimeMillis += elapsed; + } + if (screenOn) { + mLastScreenOnTimeMillis = now; + } + } + + public void noteScreenOn() { + mScreenOn = true; + mLastScreenOnTimeMillis = SystemClock.elapsedRealtime(); + } + + public void noteScreenOff() { + if (!mScreenOn) { + Log.w(TAG, "noteScreenOff without mScreenOn, ignored"); + return; + } + updateScreenOnTime(false); + mScreenOn = false; + } + + @Override public long getBatteryScreenOnTime() { + return mBatteryScreenOnTimeMillis; + } + + @Override public long getPluggedScreenOnTime() { + return mPluggedScreenOnTimeMillis; + } + @Override public SparseArray<? extends BatteryStats.Uid> getUidStats() { return mUidStats; @@ -314,6 +397,14 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { * The statistics associated with a particular uid. */ public final class Uid extends BatteryStats.Uid { + + final int mUid; + long mLoadedTcpBytesReceived; + long mLoadedTcpBytesSent; + long mTcpBytesReceivedAtLastUnplug; + long mTcpBytesSentAtLastUnplug; + + private final byte[] mBuf = new byte[16]; /** * The statistics we have collected for this uid's wake locks. @@ -334,6 +425,10 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { * The statistics we have collected for this uid's processes. */ final HashMap<String, Pkg> mPackageStats = new HashMap<String, Pkg>(); + + public Uid(int uid) { + mUid = uid; + } @Override public Map<String, ? extends BatteryStats.Uid.Wakelock> getWakelockStats() { @@ -354,6 +449,42 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { public Map<String, ? extends BatteryStats.Uid.Pkg> getPackageStats() { return mPackageStats; } + + public int getUid() { + return mUid; + } + + public long getTcpBytesReceived(int which) { + long current = NetStat.getUidRxBytes(mUid); + + if (which == STATS_CURRENT) { + return current; + } else if (which == STATS_LAST) { + return mLoadedTcpBytesReceived; + } else if (which == STATS_UNPLUGGED) { + return current - mTcpBytesReceivedAtLastUnplug; + } else if (which == STATS_TOTAL) { + return mLoadedTcpBytesReceived + current; + } else { + throw new IllegalArgumentException("which = " + which); + } + } + + public long getTcpBytesSent(int which) { + long current = NetStat.getUidTxBytes(mUid); + + if (which == STATS_CURRENT) { + return current; + } else if (which == STATS_LAST) { + return mLoadedTcpBytesSent; + } else if (which == STATS_UNPLUGGED) { + return current - mTcpBytesSentAtLastUnplug; + } else if (which == STATS_TOTAL) { + return mLoadedTcpBytesSent + current; + } else { + throw new IllegalArgumentException("which = " + which); + } + } void writeToParcelLocked(Parcel out) { out.writeInt(mWakelockStats.size()); @@ -366,6 +497,7 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { out.writeInt(mSensorStats.size()); for (Map.Entry<Integer, Uid.Sensor> sensorEntry : mSensorStats.entrySet()) { out.writeInt(sensorEntry.getKey()); + out.writeString(sensorEntry.getValue().getName()); Uid.Sensor sensor = sensorEntry.getValue(); sensor.writeToParcelLocked(out); } @@ -383,6 +515,11 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { Uid.Pkg pkg = pkgEntry.getValue(); pkg.writeToParcelLocked(out); } + + out.writeLong(mLoadedTcpBytesReceived); + out.writeLong(mLoadedTcpBytesSent); + out.writeLong(mTcpBytesReceivedAtLastUnplug); + out.writeLong(mTcpBytesSentAtLastUnplug); } void readFromParcelLocked(Parcel in) { @@ -399,7 +536,8 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { mSensorStats.clear(); for (int k = 0; k < numSensors; k++) { int sensorNumber = in.readInt(); - Uid.Sensor sensor = new Sensor(); + String name = in.readString(); + Uid.Sensor sensor = new Sensor(name); sensor.readFromParcelLocked(in); mSensorStats.put(sensorNumber, sensor); } @@ -421,6 +559,11 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { pkg.readFromParcelLocked(in); mPackageStats.put(packageName, pkg); } + + mLoadedTcpBytesReceived = in.readLong(); + mLoadedTcpBytesSent = in.readLong(); + mTcpBytesReceivedAtLastUnplug = in.readLong(); + mTcpBytesSentAtLastUnplug = in.readLong(); } /** @@ -499,7 +642,13 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } public final class Sensor extends BatteryStats.Uid.Sensor { + static final int GPS = -10000; // Treat GPS as a sensor + final String mName; Timer sensorTime; + + public Sensor(String name) { + mName = name; + } private Timer readTimerFromParcel(Parcel in) { if (in.readInt() == 0) { @@ -530,6 +679,10 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { public Timer getSensorTime() { return sensorTime; } + + public String getName() { + return mName; + } } /** @@ -1039,19 +1192,19 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } } - public Timer getSensorTimerLocked(int sensor, boolean create) { + public Timer getSensorTimerLocked(String name, int sensor, boolean create) { Integer sId = Integer.valueOf(sensor); Sensor se = mSensorStats.get(sId); if (se == null) { if (!create) { return null; } - se = new Sensor(); + se = new Sensor(name); mSensorStats.put(sId, se); } Timer t = se.sensorTime; if (t == null) { - t = new Timer(0, mSensorTimers); + t = new Timer(BatteryStats.SENSOR, mSensorTimers); se.sensorTime = t; } return t; @@ -1071,20 +1224,34 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } } - public void noteStartSensor(int sensor) { - Timer t = getSensorTimerLocked(sensor, true); + public void noteStartSensor(String name, int sensor) { + Timer t = getSensorTimerLocked(name, sensor, true); if (t != null) { t.startRunningLocked(BatteryStatsImpl.this); } } - public void noteStopSensor(int sensor) { + public void noteStopSensor(String name, int sensor) { // Don't create a timer if one doesn't already exist - Timer t = getSensorTimerLocked(sensor, false); + Timer t = getSensorTimerLocked(name, sensor, false); if (t != null) { t.stopRunningLocked(BatteryStatsImpl.this); } } + + public void noteStartGps() { + Timer t = getSensorTimerLocked("GPS", Sensor.GPS, true); + if (t != null) { + t.startRunningLocked(BatteryStatsImpl.this); + } + } + + public void noteStopGps() { + Timer t = getSensorTimerLocked("GPS", Sensor.GPS, false); + if (t != null) { + t.stopRunningLocked(BatteryStatsImpl.this); + } + } public BatteryStatsImpl getBatteryStats() { return BatteryStatsImpl.this; @@ -1100,6 +1267,9 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { mTrackBatteryPastRealtime = 0; mUptimeStart = mTrackBatteryUptimeStart = SystemClock.uptimeMillis() * 1000; mRealtimeStart = mTrackBatteryRealtimeStart = SystemClock.elapsedRealtime() * 1000; + + mUnpluggedBatteryUptime = getBatteryUptimeLocked(mUptimeStart); + mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(mRealtimeStart); } public BatteryStatsImpl(Parcel p) { @@ -1119,13 +1289,21 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { public void setOnBattery(boolean onBattery) { synchronized(this) { if (mOnBattery != onBattery) { + if (mScreenOn) { + updateScreenOnTime(true); + } + long uptime = SystemClock.uptimeMillis() * 1000; long mSecRealtime = SystemClock.elapsedRealtime(); long realtime = mSecRealtime * 1000; if (onBattery) { - mTrackBatteryUptimeStart = uptime; - mTrackBatteryRealtimeStart = realtime; + mTrackBatteryUptimeStart = getBatteryUptime(uptime); + mTrackBatteryRealtimeStart = getBatteryRealtime(realtime); + unplugTcpCounters(); unplugTimers(); + + mUnpluggedBatteryUptime = getBatteryUptime(uptime); + mUnpluggedBatteryRealtime = getBatteryRealtime(realtime); } else { mTrackBatteryPastUptime += uptime - mTrackBatteryUptimeStart; mTrackBatteryPastRealtime += realtime - mTrackBatteryRealtimeStart; @@ -1172,14 +1350,16 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { @Override public long computeBatteryUptime(long curTime, int which) { + long uptime = getBatteryUptime(curTime); switch (which) { case STATS_TOTAL: - return mBatteryUptime + getBatteryUptime(curTime); + return mBatteryUptime + uptime; case STATS_LAST: return mBatteryLastUptime; case STATS_CURRENT: + return uptime; case STATS_UNPLUGGED: - return getBatteryUptime(curTime); + return uptime - mUnpluggedBatteryUptime; } return 0; } @@ -1192,8 +1372,9 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { case STATS_LAST: return mBatteryLastRealtime; case STATS_CURRENT: - case STATS_UNPLUGGED: return getBatteryRealtimeLocked(curTime); + case STATS_UNPLUGGED: + return getBatteryRealtimeLocked(curTime) - mUnpluggedBatteryRealtime; } return 0; } @@ -1234,7 +1415,7 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { public Uid getUidStatsLocked(int uid) { Uid u = mUidStats.get(uid); if (u == null) { - u = new Uid(); + u = new Uid(uid); mUidStats.put(uid, u); } return u; @@ -1246,7 +1427,7 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { public void removeUidStatsLocked(int uid) { mUidStats.remove(uid); } - + /** * Retrieve the statistics object for a particular process, creating * if needed. @@ -1388,15 +1569,21 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { mRealtime = in.readLong(); mLastRealtime = in.readLong(); mStartCount++; + + if (version >= 14) { + mBatteryScreenOnTimeMillis = in.readLong(); + mPluggedScreenOnTimeMillis = in.readLong(); + mScreenOn = false; + } final int NU = in.readInt(); - for (int iu=0; iu<NU; iu++) { + for (int iu = 0; iu < NU; iu++) { int uid = in.readInt(); - Uid u = new Uid(); + Uid u = new Uid(uid); mUidStats.put(uid, u); int NW = in.readInt(); - for (int iw=0; iw<NW; iw++) { + for (int iw = 0; iw < NW; iw++) { String wlName = in.readString(); if (in.readInt() != 0) { u.getWakeTimerLocked(wlName, WAKE_TYPE_FULL).readSummaryFromParcelLocked(in); @@ -1411,16 +1598,21 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { if (version >= 12) { int NSE = in.readInt(); - for (int is=0; is<NSE; is++) { + for (int is = 0; is < NSE; is++) { int seNumber = in.readInt(); + String seName = "<unknown>"; + if (version >= 14) { + seName = in.readString(); + } if (in.readInt() != 0) { - u.getSensorTimerLocked(seNumber, true).readSummaryFromParcelLocked(in); + u.getSensorTimerLocked(seName, seNumber, true) + .readSummaryFromParcelLocked(in); } } } int NP = in.readInt(); - for (int ip=0; ip<NP; ip++) { + for (int ip = 0; ip < NP; ip++) { String procName = in.readString(); Uid.Proc p = u.getProcessStatsLocked(procName); p.mUserTime = p.mLoadedUserTime = in.readLong(); @@ -1432,13 +1624,13 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } NP = in.readInt(); - for (int ip=0; ip<NP; ip++) { + for (int ip = 0; ip < NP; ip++) { String pkgName = in.readString(); Uid.Pkg p = u.getPackageStatsLocked(pkgName); p.mWakeups = p.mLoadedWakeups = in.readInt(); p.mLastWakeups = in.readInt(); final int NS = in.readInt(); - for (int is=0; is<NS; is++) { + for (int is = 0; is < NS; is++) { String servName = in.readString(); Uid.Pkg.Serv s = u.getServiceStatsLocked(pkgName, servName); s.mStartTime = s.mLoadedStartTime = in.readLong(); @@ -1449,6 +1641,11 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { s.mLastLaunches = in.readInt(); } } + + if (version >= 14) { + u.mLoadedTcpBytesReceived = in.readLong(); + u.mLoadedTcpBytesSent = in.readLong(); + } } } @@ -1474,10 +1671,13 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { out.writeLong(computeUptime(NOW_SYS, STATS_CURRENT)); out.writeLong(computeRealtime(NOWREAL_SYS, STATS_TOTAL)); out.writeLong(computeRealtime(NOWREAL_SYS, STATS_CURRENT)); + + out.writeLong(mBatteryScreenOnTimeMillis); + out.writeLong(mPluggedScreenOnTimeMillis); final int NU = mUidStats.size(); out.writeInt(NU); - for (int iu=0; iu<NU; iu++) { + for (int iu = 0; iu < NU; iu++) { out.writeInt(mUidStats.keyAt(iu)); Uid u = mUidStats.valueAt(iu); @@ -1516,6 +1716,7 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { : u.mSensorStats.entrySet()) { out.writeInt(ent.getKey()); Uid.Sensor se = ent.getValue(); + out.writeString(se.getName()); if (se.sensorTime != null) { out.writeInt(1); se.sensorTime.writeSummaryFromParcelLocked(out, NOW); @@ -1568,6 +1769,9 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { } } } + + out.writeLong(u.getTcpBytesReceived(STATS_TOTAL)); + out.writeLong(u.getTcpBytesSent(STATS_TOTAL)); } } @@ -1586,6 +1790,9 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { mBatteryLastUptime = in.readLong(); mBatteryRealtime = in.readLong(); mBatteryLastRealtime = in.readLong(); + mBatteryScreenOnTimeMillis = in.readLong(); + mPluggedScreenOnTimeMillis = in.readLong(); + mScreenOn = false; mUptime = in.readLong(); mUptimeStart = in.readLong(); mLastUptime = in.readLong(); @@ -1606,10 +1813,10 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { int numUids = in.readInt(); mUidStats.clear(); for (int i = 0; i < numUids; i++) { - int key = in.readInt(); - Uid uid = new Uid(); - uid.readFromParcelLocked(in); - mUidStats.append(key, uid); + int uid = in.readInt(); + Uid u = new Uid(uid); + u.readFromParcelLocked(in); + mUidStats.append(uid, u); } } @@ -1625,6 +1832,8 @@ public final class BatteryStatsImpl extends BatteryStats implements Parcelable { out.writeLong(mBatteryLastUptime); out.writeLong(mBatteryRealtime); out.writeLong(mBatteryLastRealtime); + out.writeLong(mBatteryScreenOnTimeMillis); + out.writeLong(mPluggedScreenOnTimeMillis); out.writeLong(mUptime); out.writeLong(mUptimeStart); out.writeLong(mLastUptime); diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java index 1ec74a1..bab1e21 100644 --- a/core/java/com/android/internal/os/HandlerCaller.java +++ b/core/java/com/android/internal/os/HandlerCaller.java @@ -21,7 +21,8 @@ public class HandlerCaller { public Object arg1; public Object arg2; - Object arg3; + public Object arg3; + public Object arg4; public int argi1; public int argi2; public int argi3; @@ -149,6 +150,16 @@ public class HandlerCaller { return mH.obtainMessage(what, 0, 0, args); } + public Message obtainMessageOOOO(int what, Object arg1, Object arg2, + Object arg3, Object arg4) { + SomeArgs args = obtainArgs(); + args.arg1 = arg1; + args.arg2 = arg2; + args.arg3 = arg3; + args.arg4 = arg4; + return mH.obtainMessage(what, 0, 0, args); + } + public Message obtainMessageIIII(int what, int arg1, int arg2, int arg3, int arg4) { SomeArgs args = obtainArgs(); diff --git a/core/java/com/android/internal/os/PkgUsageStats.aidl b/core/java/com/android/internal/os/PkgUsageStats.aidl new file mode 100755 index 0000000..8305271 --- /dev/null +++ b/core/java/com/android/internal/os/PkgUsageStats.aidl @@ -0,0 +1,20 @@ +/* //device/java/android/android/content/Intent.aidl +** +** Copyright 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 +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package com.android.internal.os; + +parcelable PkgUsageStats; diff --git a/core/java/com/android/internal/os/PkgUsageStats.java b/core/java/com/android/internal/os/PkgUsageStats.java new file mode 100755 index 0000000..e847878 --- /dev/null +++ b/core/java/com/android/internal/os/PkgUsageStats.java @@ -0,0 +1,60 @@ +package com.android.internal.os; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * implementation of PkgUsageStats associated with an + * application package. + * @hide + */ +public class PkgUsageStats implements Parcelable { + public String packageName; + public int launchCount; + public long usageTime; + + public static final Parcelable.Creator<PkgUsageStats> CREATOR + = new Parcelable.Creator<PkgUsageStats>() { + public PkgUsageStats createFromParcel(Parcel in) { + return new PkgUsageStats(in); + } + + public PkgUsageStats[] newArray(int size) { + return new PkgUsageStats[size]; + } + }; + + public String toString() { + return "PkgUsageStats{" + + Integer.toHexString(System.identityHashCode(this)) + + " " + packageName + "}"; + } + + public PkgUsageStats(String pkgName, int count, long time) { + packageName = pkgName; + launchCount = count; + usageTime = time; + } + + public PkgUsageStats(Parcel source) { + packageName = source.readString(); + launchCount = source.readInt(); + usageTime = source.readLong(); + } + + public PkgUsageStats(PkgUsageStats pStats) { + packageName = pStats.packageName; + launchCount = pStats.launchCount; + usageTime = pStats.usageTime; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int parcelableFlags){ + dest.writeString(packageName); + dest.writeInt(launchCount); + dest.writeLong(usageTime); + } +} diff --git a/core/java/com/android/internal/os/RecoverySystem.java b/core/java/com/android/internal/os/RecoverySystem.java new file mode 100644 index 0000000..c938610 --- /dev/null +++ b/core/java/com/android/internal/os/RecoverySystem.java @@ -0,0 +1,128 @@ +/* + * 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 com.android.internal.os; + +import android.os.FileUtils; +import android.os.Power; +import android.util.Log; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; + +/** + * Utility class for interacting with the Android recovery partition. + * The recovery partition is a small standalone system which can perform + * operations that are difficult while the main system is running, like + * upgrading system software or reformatting the data partition. + * Note that most of these operations must be run as root. + * + * @hide + */ +public class RecoverySystem { + private static final String TAG = "RecoverySystem"; // for logging + + // Used to communicate with recovery. See commands/recovery/recovery.c. + private static File RECOVERY_DIR = new File("/cache/recovery"); + private static File COMMAND_FILE = new File(RECOVERY_DIR, "command"); + private static File LOG_FILE = new File(RECOVERY_DIR, "log"); + + // Length limits for reading files. + private static int LOG_FILE_MAX_LENGTH = 8 * 1024; + + /** + * Reboot into the recovery system to install a system update. + * @param update package to install (must be in /cache or /data). + * @throws IOException if something goes wrong. + */ + public static void rebootAndUpdate(File update) throws IOException { + String path = update.getCanonicalPath(); + if (path.startsWith("/cache/")) { + path = "CACHE:" + path.substring(7); + } else if (path.startsWith("/data/")) { + path = "DATA:" + path.substring(6); + } else { + throw new IllegalArgumentException( + "Must start with /cache or /data: " + path); + } + bootCommand("--update_package=" + path); + } + + /** + * Reboot into the recovery system to wipe the /data partition. + * @param extras to add to the RECOVERY_COMPLETED intent after rebooting. + * @throws IOException if something goes wrong. + */ + public static void rebootAndWipe() throws IOException { + bootCommand("--wipe_data"); + } + + /** + * Reboot into the recovery system with the supplied argument. + * @param arg to pass to the recovery utility. + * @throws IOException if something goes wrong. + */ + private static void bootCommand(String arg) throws IOException { + RECOVERY_DIR.mkdirs(); // In case we need it + COMMAND_FILE.delete(); // In case it's not writable + LOG_FILE.delete(); + + FileWriter command = new FileWriter(COMMAND_FILE); + try { + command.write(arg); + command.write("\n"); + } finally { + command.close(); + } + + // Having written the command file, go ahead and reboot + Power.reboot("recovery"); + throw new IOException("Reboot failed (no permissions?)"); + } + + /** + * Called after booting to process and remove recovery-related files. + * @return the log file from recovery, or null if none was found. + */ + public static String handleAftermath() { + // Record the tail of the LOG_FILE + String log = null; + try { + log = FileUtils.readTextFile(LOG_FILE, -LOG_FILE_MAX_LENGTH, "...\n"); + } catch (FileNotFoundException e) { + Log.i(TAG, "No recovery log file"); + } catch (IOException e) { + Log.e(TAG, "Error reading recovery log", e); + } + + // Delete everything in RECOVERY_DIR + String[] names = RECOVERY_DIR.list(); + for (int i = 0; names != null && i < names.length; i++) { + File f = new File(RECOVERY_DIR, names[i]); + if (!f.delete()) { + Log.e(TAG, "Can't delete: " + f); + } else { + Log.i(TAG, "Deleted: " + f); + } + } + + return log; + } +} diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 011e944..b0b00b2 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -13,6 +13,8 @@ import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; +import java.lang.ref.WeakReference; + public class IInputConnectionWrapper extends IInputContext.Stub { static final String TAG = "IInputConnectionWrapper"; @@ -22,6 +24,8 @@ public class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_GET_EXTRACTED_TEXT = 40; private static final int DO_COMMIT_TEXT = 50; private static final int DO_COMMIT_COMPLETION = 55; + private static final int DO_SET_SELECTION = 57; + private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 58; private static final int DO_SET_COMPOSING_TEXT = 60; private static final int DO_FINISH_COMPOSING_TEXT = 65; private static final int DO_SEND_KEY_EVENT = 70; @@ -33,7 +37,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_PERFORM_PRIVATE_COMMAND = 120; private static final int DO_CLEAR_META_KEY_STATES = 130; - private InputConnection mInputConnection; + private WeakReference<InputConnection> mInputConnection; private Looper mMainLooper; private Handler mH; @@ -57,17 +61,21 @@ public class IInputConnectionWrapper extends IInputContext.Stub { } public IInputConnectionWrapper(Looper mainLooper, InputConnection conn) { - mInputConnection = conn; + mInputConnection = new WeakReference<InputConnection>(conn); mMainLooper = mainLooper; mH = new MyHandler(mMainLooper); } - public void getTextAfterCursor(int length, int seq, IInputContextCallback callback) { - dispatchMessage(obtainMessageISC(DO_GET_TEXT_AFTER_CURSOR, length, seq, callback)); + public boolean isActive() { + return true; + } + + public void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback) { + dispatchMessage(obtainMessageIISC(DO_GET_TEXT_AFTER_CURSOR, length, flags, seq, callback)); } - public void getTextBeforeCursor(int length, int seq, IInputContextCallback callback) { - dispatchMessage(obtainMessageISC(DO_GET_TEXT_BEFORE_CURSOR, length, seq, callback)); + public void getTextBeforeCursor(int length, int flags, int seq, IInputContextCallback callback) { + dispatchMessage(obtainMessageIISC(DO_GET_TEXT_BEFORE_CURSOR, length, flags, seq, callback)); } public void getCursorCapsMode(int reqModes, int seq, IInputContextCallback callback) { @@ -88,6 +96,14 @@ public class IInputConnectionWrapper extends IInputContext.Stub { dispatchMessage(obtainMessageO(DO_COMMIT_COMPLETION, text)); } + public void setSelection(int start, int end) { + dispatchMessage(obtainMessageII(DO_SET_SELECTION, start, end)); + } + + public void performContextMenuAction(int id) { + dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0)); + } + public void setComposingText(CharSequence text, int newCursorPosition) { dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text)); } @@ -147,8 +163,14 @@ public class IInputConnectionWrapper extends IInputContext.Stub { case DO_GET_TEXT_AFTER_CURSOR: { SomeArgs args = (SomeArgs)msg.obj; try { - args.callback.setTextAfterCursor(mInputConnection.getTextAfterCursor(msg.arg1), - args.seq); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "getTextAfterCursor on inactive InputConnection"); + args.callback.setTextAfterCursor(null, args.seq); + return; + } + args.callback.setTextAfterCursor(ic.getTextAfterCursor( + msg.arg1, msg.arg2), args.seq); } catch (RemoteException e) { Log.w(TAG, "Got RemoteException calling setTextAfterCursor", e); } @@ -157,8 +179,14 @@ public class IInputConnectionWrapper extends IInputContext.Stub { case DO_GET_TEXT_BEFORE_CURSOR: { SomeArgs args = (SomeArgs)msg.obj; try { - args.callback.setTextBeforeCursor(mInputConnection.getTextBeforeCursor(msg.arg1), - args.seq); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "getTextBeforeCursor on inactive InputConnection"); + args.callback.setTextBeforeCursor(null, args.seq); + return; + } + args.callback.setTextBeforeCursor(ic.getTextBeforeCursor( + msg.arg1, msg.arg2), args.seq); } catch (RemoteException e) { Log.w(TAG, "Got RemoteException calling setTextBeforeCursor", e); } @@ -167,7 +195,13 @@ public class IInputConnectionWrapper extends IInputContext.Stub { case DO_GET_CURSOR_CAPS_MODE: { SomeArgs args = (SomeArgs)msg.obj; try { - args.callback.setCursorCapsMode(mInputConnection.getCursorCapsMode(msg.arg1), + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "getCursorCapsMode on inactive InputConnection"); + args.callback.setCursorCapsMode(0, args.seq); + return; + } + args.callback.setCursorCapsMode(ic.getCursorCapsMode(msg.arg1), args.seq); } catch (RemoteException e) { Log.w(TAG, "Got RemoteException calling setCursorCapsMode", e); @@ -177,7 +211,13 @@ public class IInputConnectionWrapper extends IInputContext.Stub { case DO_GET_EXTRACTED_TEXT: { SomeArgs args = (SomeArgs)msg.obj; try { - args.callback.setExtractedText(mInputConnection.getExtractedText( + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "getExtractedText on inactive InputConnection"); + args.callback.setExtractedText(null, args.seq); + return; + } + args.callback.setExtractedText(ic.getExtractedText( (ExtractedTextRequest)args.arg1, msg.arg1), args.seq); } catch (RemoteException e) { Log.w(TAG, "Got RemoteException calling setExtractedText", e); @@ -185,52 +225,130 @@ public class IInputConnectionWrapper extends IInputContext.Stub { return; } case DO_COMMIT_TEXT: { - mInputConnection.commitText((CharSequence)msg.obj, msg.arg1); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitText on inactive InputConnection"); + return; + } + ic.commitText((CharSequence)msg.obj, msg.arg1); + return; + } + case DO_SET_SELECTION: { + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "setSelection on inactive InputConnection"); + return; + } + ic.setSelection(msg.arg1, msg.arg2); + return; + } + case DO_PERFORM_CONTEXT_MENU_ACTION: { + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "performContextMenuAction on inactive InputConnection"); + return; + } + ic.performContextMenuAction(msg.arg1); return; } case DO_COMMIT_COMPLETION: { - mInputConnection.commitCompletion((CompletionInfo)msg.obj); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitCompletion on inactive InputConnection"); + return; + } + ic.commitCompletion((CompletionInfo)msg.obj); return; } case DO_SET_COMPOSING_TEXT: { - mInputConnection.setComposingText((CharSequence)msg.obj, msg.arg1); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "setComposingText on inactive InputConnection"); + return; + } + ic.setComposingText((CharSequence)msg.obj, msg.arg1); return; } case DO_FINISH_COMPOSING_TEXT: { - mInputConnection.finishComposingText(); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "finishComposingText on inactive InputConnection"); + return; + } + ic.finishComposingText(); return; } case DO_SEND_KEY_EVENT: { - mInputConnection.sendKeyEvent((KeyEvent)msg.obj); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "sendKeyEvent on inactive InputConnection"); + return; + } + ic.sendKeyEvent((KeyEvent)msg.obj); return; } case DO_CLEAR_META_KEY_STATES: { - mInputConnection.clearMetaKeyStates(msg.arg1); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "clearMetaKeyStates on inactive InputConnection"); + return; + } + ic.clearMetaKeyStates(msg.arg1); return; } case DO_DELETE_SURROUNDING_TEXT: { - mInputConnection.deleteSurroundingText(msg.arg1, msg.arg2); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "deleteSurroundingText on inactive InputConnection"); + return; + } + ic.deleteSurroundingText(msg.arg1, msg.arg2); return; } case DO_BEGIN_BATCH_EDIT: { - mInputConnection.beginBatchEdit(); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "beginBatchEdit on inactive InputConnection"); + return; + } + ic.beginBatchEdit(); return; } case DO_END_BATCH_EDIT: { - mInputConnection.beginBatchEdit(); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "endBatchEdit on inactive InputConnection"); + return; + } + ic.endBatchEdit(); return; } case DO_HIDE_STATUS_ICON: { - mInputConnection.hideStatusIcon(); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "hideStatusIcon on inactive InputConnection"); + return; + } + ic.hideStatusIcon(); return; } case DO_SHOW_STATUS_ICON: { - mInputConnection.showStatusIcon((String)msg.obj, msg.arg1); + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "showStatusIcon on inactive InputConnection"); + return; + } + ic.showStatusIcon((String)msg.obj, msg.arg1); return; } case DO_PERFORM_PRIVATE_COMMAND: { + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "performPrivateCommand on inactive InputConnection"); + return; + } SomeArgs args = (SomeArgs)msg.obj; - mInputConnection.performPrivateCommand((String)args.arg1, + ic.performPrivateCommand((String)args.arg1, (Bundle)args.arg2); return; } @@ -257,6 +375,13 @@ public class IInputConnectionWrapper extends IInputContext.Stub { return mH.obtainMessage(what, arg1, 0, args); } + Message obtainMessageIISC(int what, int arg1, int arg2, int seq, IInputContextCallback callback) { + SomeArgs args = new SomeArgs(); + args.callback = callback; + args.seq = seq; + return mH.obtainMessage(what, arg1, arg2, args); + } + Message obtainMessageIOSC(int what, int arg1, Object arg2, int seq, IInputContextCallback callback) { SomeArgs args = new SomeArgs(); diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl index b048ce2..7cc8ada 100644 --- a/core/java/com/android/internal/view/IInputContext.aidl +++ b/core/java/com/android/internal/view/IInputContext.aidl @@ -29,9 +29,9 @@ import com.android.internal.view.IInputContextCallback; * {@hide} */ oneway interface IInputContext { - void getTextBeforeCursor(int length, int seq, IInputContextCallback callback); + void getTextBeforeCursor(int length, int flags, int seq, IInputContextCallback callback); - void getTextAfterCursor(int length, int seq, IInputContextCallback callback); + void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback); void getCursorCapsMode(int reqModes, int seq, IInputContextCallback callback); @@ -48,6 +48,10 @@ import com.android.internal.view.IInputContextCallback; void commitCompletion(in CompletionInfo completion); + void setSelection(int start, int end); + + void performContextMenuAction(int id); + void beginBatchEdit(); void endBatchEdit(); diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl index f650713..9b00402 100644 --- a/core/java/com/android/internal/view/IInputMethod.aidl +++ b/core/java/com/android/internal/view/IInputMethod.aidl @@ -38,9 +38,9 @@ oneway interface IInputMethod { void unbindInput(); - void startInput(in EditorInfo attribute); + void startInput(in IInputContext inputContext, in EditorInfo attribute); - void restartInput(in EditorInfo attribute); + void restartInput(in IInputContext inputContext, in EditorInfo attribute); void createSession(IInputMethodCallback callback); @@ -48,7 +48,7 @@ oneway interface IInputMethod { void revokeSession(IInputMethodSession session); - void showSoftInput(boolean explicit); + void showSoftInput(int flags); void hideSoftInput(); } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 2a15bdb..2f5cd14 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -35,7 +35,8 @@ interface IInputMethodManager { void removeClient(in IInputMethodClient client); InputBindResult startInput(in IInputMethodClient client, - in EditorInfo attribute, boolean initial, boolean needResult); + IInputContext inputContext, in EditorInfo attribute, + boolean initial, boolean needResult); void finishInput(in IInputMethodClient client); void showSoftInput(in IInputMethodClient client, int flags); void hideSoftInput(in IInputMethodClient client, int flags); diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index a9ba5f6..af4ad25 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -151,11 +151,11 @@ public class InputConnectionWrapper implements InputConnection { mIInputContext = inputContext; } - public CharSequence getTextAfterCursor(int length) { + public CharSequence getTextAfterCursor(int length, int flags) { CharSequence value = null; try { InputContextCallback callback = InputContextCallback.getInstance(); - mIInputContext.getTextAfterCursor(length, callback.mSeq, callback); + mIInputContext.getTextAfterCursor(length, flags, callback.mSeq, callback); synchronized (callback) { callback.waitForResultLocked(); if (callback.mHaveValue) { @@ -169,11 +169,11 @@ public class InputConnectionWrapper implements InputConnection { return value; } - public CharSequence getTextBeforeCursor(int length) { + public CharSequence getTextBeforeCursor(int length, int flags) { CharSequence value = null; try { InputContextCallback callback = InputContextCallback.getInstance(); - mIInputContext.getTextBeforeCursor(length, callback.mSeq, callback); + mIInputContext.getTextBeforeCursor(length, flags, callback.mSeq, callback); synchronized (callback) { callback.waitForResultLocked(); if (callback.mHaveValue) { @@ -241,6 +241,24 @@ public class InputConnectionWrapper implements InputConnection { } } + public boolean setSelection(int start, int end) { + try { + mIInputContext.setSelection(start, end); + return true; + } catch (RemoteException e) { + return false; + } + } + + public boolean performContextMenuAction(int id) { + try { + mIInputContext.performContextMenuAction(id); + return true; + } catch (RemoteException e) { + return false; + } + } + public boolean setComposingText(CharSequence text, int newCursorPosition) { try { mIInputContext.setComposingText(text, newCursorPosition); diff --git a/core/java/com/android/internal/view/menu/IconMenuItemView.java b/core/java/com/android/internal/view/menu/IconMenuItemView.java index 558a4c3..9e1f2ae 100644 --- a/core/java/com/android/internal/view/menu/IconMenuItemView.java +++ b/core/java/com/android/internal/view/menu/IconMenuItemView.java @@ -142,9 +142,6 @@ public final class IconMenuItemView extends TextView implements MenuView.ItemVie } void setCaptionMode(boolean shortcut) { - - mShortcutCaptionMode = shortcut && (mItemData.shouldShowShortcut()); - /* * If there is no item model, don't do any of the below (for example, * the 'More' item doesn't have a model) @@ -153,6 +150,8 @@ public final class IconMenuItemView extends TextView implements MenuView.ItemVie return; } + mShortcutCaptionMode = shortcut && (mItemData.shouldShowShortcut()); + CharSequence text = mItemData.getTitleForItemView(this); if (mShortcutCaptionMode) { diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java index 32513cd..e155875 100644 --- a/core/java/com/android/internal/view/menu/ListMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java @@ -220,7 +220,7 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView mRadioButton = (RadioButton) inflater.inflate(com.android.internal.R.layout.list_menu_item_radio, this, false); - addView(mRadioButton, 0); + addView(mRadioButton); } private void insertCheckBox() { @@ -228,7 +228,7 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView mCheckBox = (CheckBox) inflater.inflate(com.android.internal.R.layout.list_menu_item_checkbox, this, false); - addView(mCheckBox, 0); + addView(mCheckBox); } public boolean prefersCondensedTitle() { diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index a2673a5..648d944 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -16,175 +16,74 @@ package com.android.internal.widget; -import android.content.res.TypedArray; import android.os.Bundle; -import android.os.Handler; import android.text.Editable; -import android.text.Selection; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.TextUtils; import android.text.method.KeyListener; import android.util.Log; -import android.util.LogPrinter; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.widget.TextView; -class ComposingText { -} - public class EditableInputConnection extends BaseInputConnection { private static final boolean DEBUG = false; private static final String TAG = "EditableInputConnection"; - public static final Object COMPOSING = new ComposingText(); - private final TextView mTextView; - private Object[] mDefaultComposingSpans; - public EditableInputConnection(TextView textview) { - super(textview); + super(textview, false); mTextView = textview; } - public static final void removeComposingSpans(Spannable text) { - text.removeSpan(COMPOSING); - Object[] sps = text.getSpans(0, text.length(), Object.class); - if (sps != null) { - for (int i=sps.length-1; i>=0; i--) { - Object o = sps[i]; - if ((text.getSpanFlags(o)&Spanned.SPAN_COMPOSING) != 0) { - text.removeSpan(o); - } - } - } - } - - public static void setComposingSpans(Spannable text) { - final Object[] sps = text.getSpans(0, text.length(), Object.class); - if (sps != null) { - for (int i=sps.length-1; i>=0; i--) { - final Object o = sps[i]; - if (o == COMPOSING) { - text.removeSpan(o); - continue; - } - final int fl = text.getSpanFlags(o); - if ((fl&(Spanned.SPAN_COMPOSING|Spanned.SPAN_POINT_MARK_MASK)) - != (Spanned.SPAN_COMPOSING|Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)) { - text.setSpan(o, text.getSpanStart(o), text.getSpanEnd(o), - (fl&Spanned.SPAN_POINT_MARK_MASK) - | Spanned.SPAN_COMPOSING - | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } + public Editable getEditable() { + TextView tv = mTextView; + if (tv != null) { + return tv.getEditableText(); } - - text.setSpan(COMPOSING, 0, text.length(), - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING); - } - - public static int getComposingSpanStart(Spannable text) { - return text.getSpanStart(COMPOSING); + return null; } - public static int getComposingSpanEnd(Spannable text) { - return text.getSpanEnd(COMPOSING); + public boolean beginBatchEdit() { + mTextView.beginBatchEdit(); + return true; } - public boolean setComposingText(CharSequence text, int newCursorPosition) { - if (DEBUG) Log.v(TAG, "setComposingText " + text); - replaceText(text, newCursorPosition, true); + public boolean endBatchEdit() { + mTextView.endBatchEdit(); return true; } - - public boolean finishComposingText() { - if (DEBUG) Log.v(TAG, "finishComposingText"); + + public boolean clearMetaKeyStates(int states) { final Editable content = getEditable(); - if (content != null) { - removeComposingSpans(content); - } + if (content == null) return false; + KeyListener kl = mTextView.getKeyListener(); + if (kl != null) kl.clearMetaKeyState(mTextView, content, states); return true; } - public boolean commitText(CharSequence text, int newCursorPosition) { - if (DEBUG) Log.v(TAG, "commitText " + text); - replaceText(text, newCursorPosition, false); - return true; - } - public boolean commitCompletion(CompletionInfo text) { if (DEBUG) Log.v(TAG, "commitCompletion " + text); + mTextView.beginBatchEdit(); mTextView.onCommitCompletion(text); + mTextView.endBatchEdit(); return true; } - public CharSequence getTextBeforeCursor(int length) { - final Editable content = getEditable(); - if (content == null) return null; - - int a = Selection.getSelectionStart(content); - int b = Selection.getSelectionEnd(content); - - if (a > b) { - int tmp = a; - a = b; - b = tmp; - } - - if (length > a) { - length = a; - } - - return content.subSequence(a - length, a); - } - - public CharSequence getTextAfterCursor(int length) { - final Editable content = getEditable(); - if (content == null) return null; - - int a = Selection.getSelectionStart(content); - int b = Selection.getSelectionEnd(content); - - if (a > b) { - int tmp = a; - a = b; - b = tmp; - } - - if (b + length > content.length()) { - length = content.length() - b; - } - - return content.subSequence(b, b + length); - } - - public int getCursorCapsMode(int reqModes) { - final Editable content = getEditable(); - if (content == null) return 0; - - int a = Selection.getSelectionStart(content); - int b = Selection.getSelectionEnd(content); - - if (a > b) { - int tmp = a; - a = b; - b = tmp; - } - - return TextUtils.getCapsMode(content, a, reqModes); + public boolean performContextMenuAction(int id) { + if (DEBUG) Log.v(TAG, "performContextMenuAction " + id); + mTextView.beginBatchEdit(); + mTextView.onTextContextMenuItem(id); + mTextView.endBatchEdit(); + return true; } public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { if (mTextView != null) { ExtractedText et = new ExtractedText(); if (mTextView.extractText(request, et)) { - if ((flags&EXTRACTED_TEXT_MONITOR) != 0) { + if ((flags&GET_EXTRACTED_TEXT_MONITOR) != 0) { mTextView.setExtracting(request); } return et; @@ -193,173 +92,8 @@ public class EditableInputConnection extends BaseInputConnection { return null; } - public boolean deleteSurroundingText(int leftLength, int rightLength) { - if (DEBUG) Log.v(TAG, "deleteSurroundingText " + leftLength - + " / " + rightLength); - final Editable content = getEditable(); - if (content == null) return false; - - int a = Selection.getSelectionStart(content); - int b = Selection.getSelectionEnd(content); - - if (a > b) { - int tmp = a; - a = b; - b = tmp; - } - - // ignore the composing text. - int ca = content.getSpanStart(COMPOSING); - int cb = content.getSpanEnd(COMPOSING); - if (cb < ca) { - int tmp = ca; - ca = cb; - cb = tmp; - } - if (ca != -1 && cb != -1) { - if (ca < a) a = ca; - if (cb > b) b = cb; - } - - int deleted = 0; - - if (leftLength > 0) { - int start = a - leftLength; - if (start < 0) start = 0; - content.delete(start, a); - deleted = a - start; - } - - if (rightLength > 0) { - b = b - deleted; - - int end = b + rightLength; - if (end > content.length()) end = content.length(); - - content.delete(b, end); - } - - return true; - } - - public boolean beginBatchEdit() { - if (mTextView == null) return false; - mTextView.onBeginBatchEdit(); - return true; - } - - public boolean endBatchEdit() { - if (mTextView == null) return false; - mTextView.onEndBatchEdit(); - return true; - } - - public boolean clearMetaKeyStates(int states) { - final Editable content = getEditable(); - if (content == null) return false; - KeyListener kl = mTextView.getKeyListener(); - if (kl != null) kl.clearMetaKeyState(mTextView, content, states); - return true; - } - public boolean performPrivateCommand(String action, Bundle data) { - if (mTextView == null) return false; mTextView.onPrivateIMECommand(action, data); return true; } - - private Editable getEditable() { - TextView tv = mTextView; - if (tv != null) { - return tv.getEditableText(); - } - return null; - } - - private void replaceText(CharSequence text, int newCursorPosition, - boolean composing) { - final Editable content = getEditable(); - - // delete composing text set previously. - int a = content.getSpanStart(COMPOSING); - int b = content.getSpanEnd(COMPOSING); - - if (DEBUG) Log.v(TAG, "Composing span: " + a + " to " + b); - - if (b < a) { - int tmp = a; - a = b; - b = tmp; - } - - if (a != -1 && b != -1) { - removeComposingSpans(content); - } else { - a = Selection.getSelectionStart(content); - b = Selection.getSelectionEnd(content); - if (a >=0 && b>= 0 && a != b) { - if (b < a) { - int tmp = a; - a = b; - b = tmp; - } - } - } - - if (composing) { - Spannable sp = null; - if (!(text instanceof Spannable)) { - sp = new SpannableStringBuilder(text); - text = sp; - if (mDefaultComposingSpans == null) { - TypedArray ta = mTextView.getContext().getTheme() - .obtainStyledAttributes(new int[] { - com.android.internal.R.attr.candidatesTextStyleSpans - }); - CharSequence style = ta.getText(0); - ta.recycle(); - if (style != null && style instanceof Spanned) { - mDefaultComposingSpans = ((Spanned)style).getSpans( - 0, style.length(), Object.class); - } - } - if (mDefaultComposingSpans != null) { - for (int i = 0; i < mDefaultComposingSpans.length; ++i) { - sp.setSpan(mDefaultComposingSpans[i], 0, sp.length(), - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - } else { - sp = (Spannable)text; - } - setComposingSpans(sp); - } - - // Adjust newCursorPosition to be relative the start of the text. - newCursorPosition += a; - - if (DEBUG) Log.v(TAG, "Replacing from " + a + " to " + b + " with \"" - + text + "\", composing=" + composing - + ", type=" + text.getClass().getCanonicalName()); - - if (DEBUG) { - LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG); - lp.println("Current text:"); - TextUtils.dumpSpans(content, lp, " "); - lp.println("Composing text:"); - TextUtils.dumpSpans(text, lp, " "); - } - - content.replace(a, b, text); - if (newCursorPosition < 0) newCursorPosition = 0; - if (newCursorPosition > content.length()) - newCursorPosition = content.length(); - Selection.setSelection(content, newCursorPosition); - - if (DEBUG) { - LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG); - lp.println("Final text:"); - TextUtils.dumpSpans(content, lp, " "); - } - } } diff --git a/core/java/com/android/internal/widget/SlidingDrawer.java b/core/java/com/android/internal/widget/SlidingDrawer.java deleted file mode 100644 index a4045d5..0000000 --- a/core/java/com/android/internal/widget/SlidingDrawer.java +++ /dev/null @@ -1,924 +0,0 @@ -/* - * 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 com.android.internal.widget; - -import android.view.ViewGroup; -import android.view.View; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.SoundEffectConstants; -import android.content.Context; -import android.content.res.TypedArray; -import android.util.AttributeSet; -import android.graphics.Canvas; -import android.graphics.Rect; -import android.graphics.Bitmap; -import android.os.SystemClock; -import android.os.Handler; -import android.os.Message; -import com.android.internal.R; - -/** - * SlidingDrawer hides content out of the screen and allows the user to drag a handle - * to bring the content on screen. SlidingDrawer can be used vertically or horizontally. - * - * A special widget composed of two children views: the handle, that the users drags, - * and the content, attached to the handle and dragged with it. - * - * SlidingDrawer should be used as an overlay inside layouts. This means SlidingDrawer - * should only be used inside of a FrameLayout or a RelativeLayout for instance. The - * size of the SlidingDrawer defines how much space the content will occupy once slid - * out so SlidingDrawer should usually use fill_parent for both its dimensions. - * - * Inside an XML layout, SlidingDrawer must define the id of the handle and of the - * content: - * - * <pre class="prettyprint"> - * <com.android.internal.widget.SlidingDrawer - * android:id="@+id/drawer" - * android:layout_width="fill_parent" - * android:layout_height="fill_parent" - * - * android:handle="@+id/handle" - * android:content="@+id/content"> - * - * <ImageView - * android:id="@id/handle" - * android:layout_width="88dip" - * android:layout_height="44dip" /> - * - * <GridView - * android:id="@id/content" - * android:layout_width="fill_parent" - * android:layout_height="fill_parent" /> - * - * </com.android.internal.widget.SlidingDrawer> - * </pre> - * - * @attr ref com.android.internal.R.styleable#SlidingDrawer_content - * @attr ref com.android.internal.R.styleable#SlidingDrawer_handle - * @attr ref com.android.internal.R.styleable#SlidingDrawer_topOffset - * @attr ref com.android.internal.R.styleable#SlidingDrawer_bottomOffset - * @attr ref com.android.internal.R.styleable#SlidingDrawer_orientation - * @attr ref com.android.internal.R.styleable#SlidingDrawer_allowSingleTap - * @attr ref com.android.internal.R.styleable#SlidingDrawer_animateOnClick - */ -public class SlidingDrawer extends ViewGroup { - public static final int ORIENTATION_HORIZONTAL = 0; - public static final int ORIENTATION_VERTICAL = 1; - - private static final int TAP_THRESHOLD = 6; - private static final float MAXIMUM_TAP_VELOCITY = 100.0f; - private static final float MAXIMUM_MINOR_VELOCITY = 150.0f; - private static final float MAXIMUM_MAJOR_VELOCITY = 200.0f; - private static final float MAXIMUM_ACCELERATION = 2000.0f; - private static final int VELOCITY_UNITS = 1000; - private static final int MSG_ANIMATE = 1000; - private static final int ANIMATION_FRAME_DURATION = 1000 / 60; - - private static final int EXPANDED_FULL_OPEN = -10001; - private static final int COLLAPSED_FULL_CLOSED = -10002; - - private final int mHandleId; - private final int mContentId; - - private View mHandle; - private View mContent; - - private final Rect mFrame = new Rect(); - private final Rect mInvalidate = new Rect(); - private boolean mTracking; - private boolean mLocked; - - private VelocityTracker mVelocityTracker; - - private boolean mVertical; - private boolean mExpanded; - private int mBottomOffset; - private int mTopOffset; - private int mHandleHeight; - private int mHandleWidth; - - private OnDrawerOpenListener mOnDrawerOpenListener; - private OnDrawerCloseListener mOnDrawerCloseListener; - private OnDrawerScrollListener mOnDrawerScrollListener; - - private final Handler mHandler = new SlidingHandler(); - private float mAnimatedAcceleration; - private float mAnimatedVelocity; - private float mAnimationPosition; - private long mAnimationLastTime; - private long mCurrentAnimationTime; - private int mTouchDelta; - private boolean mAnimating; - private boolean mAllowSingleTap; - private boolean mAnimateOnClick; - - /** - * Callback invoked when the drawer is opened. - */ - public static interface OnDrawerOpenListener { - /** - * Invoked when the drawer becomes fully open. - */ - public void onDrawerOpened(); - } - - /** - * Callback invoked when the drawer is closed. - */ - public static interface OnDrawerCloseListener { - /** - * Invoked when the drawer becomes fully closed. - */ - public void onDrawerClosed(); - } - - /** - * Callback invoked when the drawer is scrolled. - */ - public static interface OnDrawerScrollListener { - /** - * Invoked when the user starts dragging/flinging the drawer's handle. - */ - public void onScrollStarted(); - - /** - * Invoked when the user stops dragging/flinging the drawer's handle. - */ - public void onScrollEnded(); - } - - /** - * Creates a new SlidingDrawer from a specified set of attributes defined in XML. - * - * @param context The application's environment. - * @param attrs The attributes defined in XML. - */ - public SlidingDrawer(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - /** - * Creates a new SlidingDrawer from a specified set of attributes defined in XML. - * - * @param context The application's environment. - * @param attrs The attributes defined in XML. - * @param defStyle The style to apply to this widget. - */ - public SlidingDrawer(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SlidingDrawer, defStyle, 0); - - int orientation = a.getInt(R.styleable.SlidingDrawer_orientation, ORIENTATION_VERTICAL); - mVertical = orientation == ORIENTATION_VERTICAL; - mBottomOffset = (int) a.getDimension(R.styleable.SlidingDrawer_bottomOffset, 0.0f); - mTopOffset = (int) a.getDimension(R.styleable.SlidingDrawer_topOffset, 0.0f); - mAllowSingleTap = a.getBoolean(R.styleable.SlidingDrawer_allowSingleTap, true); - mAnimateOnClick = a.getBoolean(R.styleable.SlidingDrawer_animateOnClick, true); - - int handleId = a.getResourceId(R.styleable.SlidingDrawer_handle, 0); - if (handleId == 0) { - throw new IllegalArgumentException("The handle attribute is required and must refer " - + "to a valid child."); - } - - int contentId = a.getResourceId(R.styleable.SlidingDrawer_content, 0); - if (contentId == 0) { - throw new IllegalArgumentException("The handle attribute is required and must refer " - + "to a valid child."); - } - - mHandleId = handleId; - mContentId = contentId; - - a.recycle(); - - setAlwaysDrawnWithCacheEnabled(false); - } - - @Override - protected void onFinishInflate() { - mHandle = findViewById(mHandleId); - if (mHandle == null) { - throw new IllegalArgumentException("The handle attribute is must refer to an" - + " existing child."); - } - mHandle.setOnClickListener(new DrawerToggler()); - - mContent = findViewById(mContentId); - if (mContent == null) { - throw new IllegalArgumentException("The content attribute is must refer to an" - + " existing child."); - } - mContent.setVisibility(View.GONE); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); - int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); - - int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); - int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); - - if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) { - throw new RuntimeException("SlidingDrawer cannot have UNSPECIFIED dimensions"); - } - - final View handle = mHandle; - measureChild(handle, widthMeasureSpec, heightMeasureSpec); - - if (mVertical) { - int height = heightSpecSize - handle.getMeasuredHeight() - mTopOffset; - mContent.measure(MeasureSpec.makeMeasureSpec(widthSpecSize, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); - } else { - int width = widthSpecSize - handle.getMeasuredWidth() - mTopOffset; - mContent.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(heightSpecSize, MeasureSpec.EXACTLY)); - } - - setMeasuredDimension(widthSpecSize, heightSpecSize); - } - - @Override - protected void dispatchDraw(Canvas canvas) { - final long drawingTime = getDrawingTime(); - final View handle = mHandle; - final boolean isVertical = mVertical; - - drawChild(canvas, handle, drawingTime); - - if (mTracking || mAnimating) { - final Bitmap cache = mContent.getDrawingCache(); - if (cache != null) { - if (isVertical) { - canvas.drawBitmap(cache, 0, handle.getBottom(), null); - } else { - canvas.drawBitmap(cache, handle.getRight(), 0, null); - } - } else { - canvas.save(); - canvas.translate(isVertical ? 0 : handle.getLeft() - mTopOffset, - isVertical ? handle.getTop() - mTopOffset : 0); - drawChild(canvas, mContent, drawingTime); - canvas.restore(); - } - } else if (mExpanded) { - drawChild(canvas, mContent, drawingTime); - } - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - if (mTracking) { - return; - } - - final int width = r - l; - final int height = b - t; - - final View handle = mHandle; - - int childWidth = handle.getMeasuredWidth(); - int childHeight = handle.getMeasuredHeight(); - - int childLeft; - int childTop; - - final View content = mContent; - - if (mVertical) { - childLeft = (width - childWidth) / 2; - childTop = mExpanded ? mTopOffset : height - childHeight + mBottomOffset; - - content.layout(0, mTopOffset + childHeight, content.getMeasuredWidth(), - mTopOffset + childHeight + content.getMeasuredHeight()); - } else { - childLeft = mExpanded ? mTopOffset : width - childWidth + mBottomOffset; - childTop = (height - childHeight) / 2; - - content.layout(mTopOffset + childWidth, 0, - mTopOffset + childWidth + content.getMeasuredWidth(), - content.getMeasuredHeight()); - } - - handle.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight); - mHandleHeight = handle.getHeight(); - mHandleWidth = handle.getWidth(); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent event) { - if (mLocked) { - return false; - } - - final int action = event.getAction(); - - float x = event.getX(); - float y = event.getY(); - - final Rect frame = mFrame; - final View handle = mHandle; - - handle.getHitRect(frame); - if (!mTracking && !frame.contains((int) x, (int) y)) { - return false; - } - - if (action == MotionEvent.ACTION_DOWN) { - if (mOnDrawerScrollListener != null) { - mOnDrawerScrollListener.onScrollStarted(); - } - mTracking = true; - - handle.setPressed(true); - // Must be called before prepareTracking() - prepareContent(); - - if (mVertical) { - final int top = mHandle.getTop(); - mTouchDelta = (int) y - top; - prepareTracking(top); - } else { - final int left = mHandle.getLeft(); - mTouchDelta = (int) x - left; - prepareTracking(left); - } - mVelocityTracker.addMovement(event); - } - - return true; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (mLocked) { - return true; - } - - if (mTracking) { - mVelocityTracker.addMovement(event); - final int action = event.getAction(); - switch (action) { - case MotionEvent.ACTION_MOVE: - moveHandle((int) (mVertical ? event.getY() : event.getX()) - mTouchDelta); - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: { - final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(VELOCITY_UNITS); - - float yVelocity = velocityTracker.getYVelocity(); - float xVelocity = velocityTracker.getXVelocity(); - boolean negative; - - final boolean vertical = mVertical; - if (vertical) { - negative = yVelocity < 0; - if (xVelocity < 0) { - xVelocity = -xVelocity; - } - if (xVelocity > MAXIMUM_MINOR_VELOCITY) { - xVelocity = MAXIMUM_MINOR_VELOCITY; - } - } else { - negative = xVelocity < 0; - if (yVelocity < 0) { - yVelocity = -yVelocity; - } - if (yVelocity > MAXIMUM_MINOR_VELOCITY) { - yVelocity = MAXIMUM_MINOR_VELOCITY; - } - } - - float velocity = (float) Math.hypot(xVelocity, yVelocity); - if (negative) { - velocity = -velocity; - } - - final int top = mHandle.getTop(); - final int left = mHandle.getLeft(); - - if (Math.abs(velocity) < MAXIMUM_TAP_VELOCITY) { - if (vertical ? (mExpanded && top < TAP_THRESHOLD + mTopOffset) || - (!mExpanded && top > mBottomOffset + mBottom - mTop - - mHandleHeight - TAP_THRESHOLD) : - (mExpanded && left < TAP_THRESHOLD + mTopOffset) || - (!mExpanded && left > mBottomOffset + mRight - mLeft - - mHandleWidth - TAP_THRESHOLD)) { - - if (mAllowSingleTap) { - playSoundEffect(SoundEffectConstants.CLICK); - - if (mExpanded) { - animateClose(vertical ? top : left); - } else { - animateOpen(vertical ? top : left); - } - } - - } else { - performFling(vertical ? top : left, velocity, false); - } - } else { - performFling(vertical ? top : left, velocity, false); - } - } - break; - } - } - - return mTracking || mAnimating || super.onTouchEvent(event); - } - - private void animateClose(int position) { - prepareTracking(position); - performFling(position, 2000.0f, true); - } - - private void animateOpen(int position) { - prepareTracking(position); - performFling(position, -2000.0f, true); - } - - private void performFling(int position, float velocity, boolean always) { - mAnimationPosition = position; - mAnimatedVelocity = velocity; - - if (mExpanded) { - if (always || (velocity > MAXIMUM_MAJOR_VELOCITY || - (position > mTopOffset + (mVertical ? mHandleHeight : mHandleWidth) && - velocity > -MAXIMUM_MAJOR_VELOCITY))) { - // We are expanded, but they didn't move sufficiently to cause - // us to retract. Animate back to the expanded position. - mAnimatedAcceleration = MAXIMUM_ACCELERATION; - if (velocity < 0) { - mAnimatedVelocity = 0; - } - } else { - // We are expanded and are now going to animate away. - mAnimatedAcceleration = -MAXIMUM_ACCELERATION; - if (velocity > 0) { - mAnimatedVelocity = 0; - } - } - } else { - if (!always && (velocity > MAXIMUM_MAJOR_VELOCITY || - (position > (mVertical ? getHeight() : getWidth()) / 2 && - velocity > -MAXIMUM_MAJOR_VELOCITY))) { - // We are collapsed, and they moved enough to allow us to expand. - mAnimatedAcceleration = MAXIMUM_ACCELERATION; - if (velocity < 0) { - mAnimatedVelocity = 0; - } - } else { - // We are collapsed, but they didn't move sufficiently to cause - // us to retract. Animate back to the collapsed position. - mAnimatedAcceleration = -MAXIMUM_ACCELERATION; - if (velocity > 0) { - mAnimatedVelocity = 0; - } - } - } - - long now = SystemClock.uptimeMillis(); - mAnimationLastTime = now; - mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION; - mAnimating = true; - mHandler.removeMessages(MSG_ANIMATE); - mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurrentAnimationTime); - stopTracking(); - } - - private void prepareTracking(int position) { - mTracking = true; - mVelocityTracker = VelocityTracker.obtain(); - boolean opening = !mExpanded; - if (opening) { - mAnimatedAcceleration = MAXIMUM_ACCELERATION; - mAnimatedVelocity = 200; - mAnimationPosition = mBottomOffset + - (mVertical ? getHeight() - mHandleHeight : getWidth() - mHandleWidth); - moveHandle((int) mAnimationPosition); - mAnimating = true; - mHandler.removeMessages(MSG_ANIMATE); - long now = SystemClock.uptimeMillis(); - mAnimationLastTime = now; - mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION; - mAnimating = true; - } else { - if (mAnimating) { - mAnimating = false; - mHandler.removeMessages(MSG_ANIMATE); - } - moveHandle(position); - } - } - - private void moveHandle(int position) { - final View handle = mHandle; - - if (mVertical) { - if (position == EXPANDED_FULL_OPEN) { - handle.offsetTopAndBottom(mTopOffset - handle.getTop()); - invalidate(); - } else if (position == COLLAPSED_FULL_CLOSED) { - handle.offsetTopAndBottom(mBottomOffset + mBottom - mTop - - mHandleHeight - handle.getTop()); - invalidate(); - } else { - final int top = handle.getTop(); - int deltaY = position - top; - if (position < mTopOffset) { - deltaY = mTopOffset - top; - } else if (deltaY > mBottomOffset + mBottom - mTop - mHandleHeight - top) { - deltaY = mBottomOffset + mBottom - mTop - mHandleHeight - top; - } - handle.offsetTopAndBottom(deltaY); - - final Rect frame = mFrame; - final Rect region = mInvalidate; - - handle.getHitRect(frame); - region.set(frame); - - region.union(frame.left, frame.top - deltaY, frame.right, frame.bottom - deltaY); - region.union(0, frame.bottom - deltaY, getWidth(), - frame.bottom - deltaY + mContent.getHeight()); - - invalidate(region); - } - } else { - if (position == EXPANDED_FULL_OPEN) { - handle.offsetLeftAndRight(mTopOffset - handle.getLeft()); - invalidate(); - } else if (position == COLLAPSED_FULL_CLOSED) { - handle.offsetLeftAndRight(mBottomOffset + mRight - mLeft - - mHandleWidth - handle.getLeft()); - invalidate(); - } else { - final int left = handle.getLeft(); - int deltaX = position - left; - if (position < mTopOffset) { - deltaX = mTopOffset - left; - } else if (deltaX > mBottomOffset + mRight - mLeft - mHandleWidth - left) { - deltaX = mBottomOffset + mRight - mLeft - mHandleWidth - left; - } - handle.offsetLeftAndRight(deltaX); - - final Rect frame = mFrame; - final Rect region = mInvalidate; - - handle.getHitRect(frame); - region.set(frame); - - region.union(frame.left - deltaX, frame.top, frame.right - deltaX, frame.bottom); - region.union(frame.right - deltaX, 0, - frame.right - deltaX + mContent.getWidth(), getHeight()); - - invalidate(region); - } - } - } - - private void prepareContent() { - if (mAnimating) { - return; - } - - // Something changed in the content, we need to honor the layout request - // before creating the cached bitmap - final View content = mContent; - if (content.isLayoutRequested()) { - if (mVertical) { - final int childHeight = mHandleHeight; - int height = mBottom - mTop - childHeight - mTopOffset; - content.measure(MeasureSpec.makeMeasureSpec(mRight - mLeft, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); - content.layout(0, mTopOffset + childHeight, content.getMeasuredWidth(), - mTopOffset + childHeight + content.getMeasuredHeight()); - } else { - final int childWidth = mHandle.getWidth(); - int width = mRight - mLeft - childWidth - mTopOffset; - content.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(mBottom - mTop, MeasureSpec.EXACTLY)); - content.layout(childWidth + mTopOffset, 0, - mTopOffset + childWidth + content.getMeasuredWidth(), - content.getMeasuredHeight()); - } - } - // Try only once... we should really loop but it's not a big deal - // if the draw was cancelled, it will only be temporary anyway - content.getViewTreeObserver().dispatchOnPreDraw(); - content.buildDrawingCache(); - - content.setVisibility(View.GONE); - } - - private void stopTracking() { - mHandle.setPressed(false); - mTracking = false; - - if (mOnDrawerScrollListener != null) { - mOnDrawerScrollListener.onScrollEnded(); - } - - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - } - - private void doAnimation() { - if (mAnimating) { - incrementAnimation(); - if (mAnimationPosition >= mBottomOffset + (mVertical ? getHeight() : getWidth()) - 1) { - mAnimating = false; - closeDrawer(); - } else if (mAnimationPosition < mTopOffset) { - mAnimating = false; - openDrawer(); - } else { - moveHandle((int) mAnimationPosition); - mCurrentAnimationTime += ANIMATION_FRAME_DURATION; - mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), - mCurrentAnimationTime); - } - } - } - - private void incrementAnimation() { - long now = SystemClock.uptimeMillis(); - float t = (now - mAnimationLastTime) / 1000.0f; // ms -> s - final float position = mAnimationPosition; - final float v = mAnimatedVelocity; // px/s - final float a = mAnimatedAcceleration; // px/s/s - mAnimationPosition = position + (v * t) + (0.5f * a * t * t); // px - mAnimatedVelocity = v + (a * t); // px/s - mAnimationLastTime = now; // ms - } - - /** - * Toggles the drawer open and close. Takes effect immediately. - * - * @see #open() - * @see #close() - * @see #animateClose() - * @see #animateOpen() - * @see #animateToggle() - */ - public void toggle() { - if (!mExpanded) { - openDrawer(); - } else { - closeDrawer(); - } - invalidate(); - requestLayout(); - } - - /** - * Toggles the drawer open and close with an animation. - * - * @see #open() - * @see #close() - * @see #animateClose() - * @see #animateOpen() - * @see #toggle() - */ - public void animateToggle() { - if (!mExpanded) { - animateOpen(); - } else { - animateClose(); - } - } - - /** - * Opens the drawer immediately. - * - * @see #toggle() - * @see #close() - * @see #animateOpen() - */ - public void open() { - openDrawer(); - invalidate(); - requestLayout(); - } - - /** - * Closes the drawer immediately. - * - * @see #toggle() - * @see #open() - * @see #animateClose() - */ - public void close() { - closeDrawer(); - invalidate(); - requestLayout(); - } - - /** - * Closes the drawer with an animation. - * - * @see #close() - * @see #open() - * @see #animateOpen() - * @see #animateToggle() - * @see #toggle() - */ - public void animateClose() { - final OnDrawerScrollListener scrollListener = mOnDrawerScrollListener; - if (scrollListener != null) { - scrollListener.onScrollStarted(); - } - prepareContent(); - animateClose(mVertical ? mHandle.getTop() : mHandle.getLeft()); - if (scrollListener != null) { - scrollListener.onScrollEnded(); - } - } - - /** - * Opens the drawer with an animation. - * - * @see #close() - * @see #open() - * @see #animateClose() - * @see #animateToggle() - * @see #toggle() - */ - public void animateOpen() { - final OnDrawerScrollListener scrollListener = mOnDrawerScrollListener; - if (scrollListener != null) { - scrollListener.onScrollStarted(); - } - prepareContent(); - animateOpen(mVertical ? mHandle.getTop() : mHandle.getLeft()); - if (scrollListener != null) { - scrollListener.onScrollEnded(); - } - } - - private void closeDrawer() { - moveHandle(COLLAPSED_FULL_CLOSED); - mContent.setVisibility(View.GONE); - - if (!mExpanded) { - return; - } - - mExpanded = false; - if (mOnDrawerCloseListener != null) { - mOnDrawerCloseListener.onDrawerClosed(); - } - } - - private void openDrawer() { - moveHandle(EXPANDED_FULL_OPEN); - mContent.setVisibility(View.VISIBLE); - // TODO: Should we uncomment to preserve memory, but increase memory churn? - // mContent.destroyDrawingCache(); - - if (mExpanded) { - return; - } - - mExpanded = true; - if (mOnDrawerOpenListener != null) { - mOnDrawerOpenListener.onDrawerOpened(); - } - } - - /** - * Sets the listener that receives a notification when the drawer becomes open. - * - * @param onDrawerOpenListener The listener to be notified when the drawer is opened. - */ - public void setOnDrawerOpenListener(OnDrawerOpenListener onDrawerOpenListener) { - mOnDrawerOpenListener = onDrawerOpenListener; - } - - /** - * Sets the listener that receives a notification when the drawer becomes close. - * - * @param onDrawerCloseListener The listener to be notified when the drawer is closed. - */ - public void setOnDrawerCloseListener(OnDrawerCloseListener onDrawerCloseListener) { - mOnDrawerCloseListener = onDrawerCloseListener; - } - - /** - * Sets the listener that receives a notification when the drawer starts or ends - * a scroll. A fling is considered as a scroll. A fling will also trigger a - * drawer opened or drawer closed event. - * - * @param onDrawerScrollListener The listener to be notified when scrolling - * starts or stops. - */ - public void setOnDrawerScrollListener(OnDrawerScrollListener onDrawerScrollListener) { - mOnDrawerScrollListener = onDrawerScrollListener; - } - - /** - * Returns the handle of the drawer. - * - * @return The View reprenseting the handle of the drawer, identified by - * the "handle" id in XML. - */ - public View getHandle() { - return mHandle; - } - - /** - * Returns the content of the drawer. - * - * @return The View reprenseting the content of the drawer, identified by - * the "content" id in XML. - */ - public View getContent() { - return mContent; - } - - /** - * Unlocks the SlidingDrawer so that touch events are processed. - * - * @see #lock() - */ - public void unlock() { - mLocked = false; - } - - /** - * Locks the SlidingDrawer so that touch events are ignores. - * - * @see #unlock() - */ - public void lock() { - mLocked = true; - } - - /** - * Indicates whether the drawer is currently fully opened. - * - * @return True if the drawer is opened, false otherwise. - */ - public boolean isOpened() { - return mExpanded; - } - - /** - * Indicates whether the drawer is scrolling or flinging. - * - * @return True if the drawer is scroller or flinging, false otherwise. - */ - public boolean isMoving() { - return mTracking || mAnimating; - } - - private class DrawerToggler implements OnClickListener { - public void onClick(View v) { - if (mLocked) { - return; - } - // mAllowSingleTap isn't relevant here; you're *always* - // allowed to open/close the drawer by clicking with the - // trackball. - - if (mAnimateOnClick) { - animateToggle(); - } else { - toggle(); - } - } - } - - private class SlidingHandler extends Handler { - public void handleMessage(Message m) { - switch (m.what) { - case MSG_ANIMATE: - doAnimation(); - break; - } - } - } -} |