method public abstract java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(java.lang.String, int);
method public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivities(android.content.Intent, int);
method public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivityOptions(android.content.ComponentName, android.content.Intent[], android.content.Intent, int);
+ method public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(android.content.Intent, int);
method public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentServices(android.content.Intent, int);
method public abstract java.util.List<android.content.pm.PermissionInfo> queryPermissionsByGroup(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract deprecated void removePackageFromPreferred(java.lang.String);
ctor public ProviderInfo();
ctor public ProviderInfo(android.content.pm.ProviderInfo);
method public int describeContents();
+ method public void dump(android.util.Printer, java.lang.String);
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
field public java.lang.String authority;
field public java.lang.CharSequence nonLocalizedLabel;
field public int preferredOrder;
field public int priority;
+ field public android.content.pm.ProviderInfo providerInfo;
field public java.lang.String resolvePackageName;
field public android.content.pm.ServiceInfo serviceInfo;
field public int specificIndex;
field public static final java.lang.String EXTRA_ERROR = "error";
field public static final java.lang.String EXTRA_INFO = "info";
field public static final java.lang.String EXTRA_LOADING = "loading";
+ field public static final java.lang.String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER";
}
public static final class DocumentsContract.Document {
method public java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(java.lang.String, int);
method public java.util.List<android.content.pm.ResolveInfo> queryIntentActivities(android.content.Intent, int);
method public java.util.List<android.content.pm.ResolveInfo> queryIntentActivityOptions(android.content.ComponentName, android.content.Intent[], android.content.Intent, int);
+ method public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(android.content.Intent, int);
method public java.util.List<android.content.pm.ResolveInfo> queryIntentServices(android.content.Intent, int);
method public java.util.List<android.content.pm.PermissionInfo> queryPermissionsByGroup(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public void removePackageFromPreferred(java.lang.String);
field public static final int SYSTEM_UI_FLAG_FULLSCREEN = 4; // 0x4
field public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2; // 0x2
field public static final int SYSTEM_UI_FLAG_IMMERSIVE = 2048; // 0x800
+ field public static final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 4096; // 0x1000
field public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024; // 0x400
field public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512; // 0x200
field public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256; // 0x100
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.Objects;
import com.android.org.conscrypt.OpenSSLSocketImpl;
+import com.google.android.collect.Lists;
import java.io.File;
import java.io.FileDescriptor;
}
}
}
+
+ @Override
+ public void scheduleInstallProvider(ProviderInfo provider) {
+ queueOrSendMessage(H.INSTALL_PROVIDER, provider);
+ }
}
private class H extends Handler {
public static final int UNSTABLE_PROVIDER_DIED = 142;
public static final int REQUEST_ASSIST_CONTEXT_EXTRAS = 143;
public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;
+ public static final int INSTALL_PROVIDER = 145;
String codeToString(int code) {
if (DEBUG_MESSAGES) {
switch (code) {
case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED";
case REQUEST_ASSIST_CONTEXT_EXTRAS: return "REQUEST_ASSIST_CONTEXT_EXTRAS";
case TRANSLUCENT_CONVERSION_COMPLETE: return "TRANSLUCENT_CONVERSION_COMPLETE";
+ case INSTALL_PROVIDER: return "INSTALL_PROVIDER";
}
}
return Integer.toString(code);
case TRANSLUCENT_CONVERSION_COMPLETE:
handleTranslucentConversionComplete((IBinder)msg.obj, msg.arg1 == 1);
break;
+ case INSTALL_PROVIDER:
+ handleInstallProvider((ProviderInfo) msg.obj);
+ break;
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
}
}
+ public void handleInstallProvider(ProviderInfo info) {
+ installContentProviders(mInitialApplication, Lists.newArrayList(info));
+ }
+
private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>();
/**
if (DEBUG_PROVIDER) Slog.v(TAG, "Cleaning up dead provider "
+ provider + " " + prc.holder.info.name);
mProviderRefCountMap.remove(provider);
- if (prc.client != null && prc.client.mNames != null) {
- for (String name : prc.client.mNames) {
- ProviderClientRecord pr = mProviderMap.get(name);
- if (pr != null && pr.mProvider.asBinder() == provider) {
- Slog.i(TAG, "Removing dead content provider: " + name);
- mProviderMap.remove(name);
- }
+ for (int i=mProviderMap.size()-1; i>=0; i--) {
+ ProviderClientRecord pr = mProviderMap.valueAt(i);
+ if (pr != null && pr.mProvider.asBinder() == provider) {
+ Slog.i(TAG, "Removing dead content provider:" + pr.mProvider.toString());
+ mProviderMap.removeAt(i);
}
}
+
if (fromClient) {
// We found out about this due to execution in our client
// code. Tell the activity manager about it now, to ensure
}
@Override
+ public List<ResolveInfo> queryIntentContentProvidersAsUser(
+ Intent intent, int flags, int userId) {
+ try {
+ return mPM.queryIntentContentProviders(intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()), flags, userId);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public List<ResolveInfo> queryIntentContentProviders(Intent intent, int flags) {
+ return queryIntentContentProvidersAsUser(intent, flags, mContext.getUserId());
+ }
+
+ @Override
public ProviderInfo resolveContentProvider(String name,
int flags) {
try {
reply.writeNoException();
return true;
}
+
+ case SCHEDULE_INSTALL_PROVIDER_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ ProviderInfo provider = ProviderInfo.CREATOR.createFromParcel(data);
+ scheduleInstallProvider(provider);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
mRemote.transact(SET_PROCESS_STATE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
data.recycle();
}
+
+ @Override
+ public void scheduleInstallProvider(ProviderInfo provider) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ provider.writeToParcel(data, 0);
+ mRemote.transact(SCHEDULE_INSTALL_PROVIDER_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
}
void scheduleTranslucentConversionComplete(IBinder token, boolean timeout)
throws RemoteException;
void setProcessState(int state) throws RemoteException;
+ void scheduleInstallProvider(ProviderInfo provider) throws RemoteException;
String descriptor = "android.app.IApplicationThread";
int REQUEST_ASSIST_CONTEXT_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+47;
int SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+48;
int SET_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+49;
+ int SCHEDULE_INSTALL_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+50;
}
List<ResolveInfo> queryIntentServices(in Intent intent,
String resolvedType, int flags, int userId);
+ List<ResolveInfo> queryIntentContentProviders(in Intent intent,
+ String resolvedType, int flags, int userId);
+
/**
* This implements getInstalledPackages via a "last returned row"
* mechanism that is not exposed in the API. This is to get around the IPC
public abstract List<ResolveInfo> queryIntentServicesAsUser(Intent intent,
int flags, int userId);
+ /** {@hide} */
+ public abstract List<ResolveInfo> queryIntentContentProvidersAsUser(
+ Intent intent, int flags, int userId);
+
+ /**
+ * Retrieve all providers that can match the given intent.
+ *
+ * @param intent An intent containing all of the desired specification
+ * (action, data, type, category, and/or component).
+ * @param flags Additional option flags.
+ * @return A List<ResolveInfo> containing one entry for each matching
+ * ProviderInfo. These are ordered from best to worst match. If
+ * there are no matching providers, an empty list is returned.
+ * @see #GET_INTENT_FILTERS
+ * @see #GET_RESOLVED_FILTER
+ */
+ public abstract List<ResolveInfo> queryIntentContentProviders(Intent intent, int flags);
+
/**
* Find a single content provider by its base path name.
*
continue;
}
- if (parser.getName().equals("meta-data")) {
+ if (parser.getName().equals("intent-filter")) {
+ ProviderIntentInfo intent = new ProviderIntentInfo(outInfo);
+ if (!parseIntent(res, parser, attrs, true, intent, outError)) {
+ return false;
+ }
+ outInfo.intents.add(intent);
+
+ } else if (parser.getName().equals("meta-data")) {
if ((outInfo.metaData=parseMetaData(res, parser, attrs,
outInfo.metaData, outError)) == null) {
return false;
return si;
}
- public final static class Provider extends Component {
+ public final static class Provider extends Component<ProviderIntentInfo> {
public final ProviderInfo info;
public boolean syncable;
}
}
+ public static final class ProviderIntentInfo extends IntentInfo {
+ public final Provider provider;
+
+ public ProviderIntentInfo(Provider provider) {
+ this.provider = provider;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("ProviderIntentInfo{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(' ');
+ provider.appendComponentShortName(sb);
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
/**
* @hide
*/
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PatternMatcher;
+import android.util.Printer;
/**
* Holds information about a specific
flags = orig.flags;
isSyncable = orig.isSyncable;
}
-
+
+ public void dump(Printer pw, String prefix) {
+ super.dumpFront(pw, prefix);
+ pw.println(prefix + "authority=" + authority);
+ pw.println(prefix + "flags=0x" + Integer.toHexString(flags));
+ }
+
public int describeContents() {
return 0;
}
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Printer;
+import android.util.Slog;
import java.text.Collator;
import java.util.Comparator;
* <intent> tags.
*/
public class ResolveInfo implements Parcelable {
+ private static final String TAG = "ResolveInfo";
+
/**
- * The activity or broadcast receiver that corresponds to this resolution match,
- * if this resolution is for an activity or broadcast receiver. One and only one of this and
- * serviceInfo must be non-null.
+ * The activity or broadcast receiver that corresponds to this resolution
+ * match, if this resolution is for an activity or broadcast receiver.
+ * Exactly one of {@link #activityInfo}, {@link #serviceInfo}, or
+ * {@link #providerInfo} will be non-null.
*/
public ActivityInfo activityInfo;
/**
- * The service that corresponds to this resolution match, if this
- * resolution is for a service. One and only one of this and
- * activityInfo must be non-null.
+ * The service that corresponds to this resolution match, if this resolution
+ * is for a service. Exactly one of {@link #activityInfo},
+ * {@link #serviceInfo}, or {@link #providerInfo} will be non-null.
*/
public ServiceInfo serviceInfo;
-
+
+ /**
+ * The provider that corresponds to this resolution match, if this
+ * resolution is for a provider. Exactly one of {@link #activityInfo},
+ * {@link #serviceInfo}, or {@link #providerInfo} will be non-null.
+ */
+ public ProviderInfo providerInfo;
+
/**
* The IntentFilter that was matched for this ResolveInfo.
*/
*/
public boolean system;
+ private ComponentInfo getComponentInfo() {
+ if (activityInfo != null) return activityInfo;
+ if (serviceInfo != null) return serviceInfo;
+ if (providerInfo != null) return providerInfo;
+ throw new IllegalStateException("Missing ComponentInfo!");
+ }
+
/**
* Retrieve the current textual label associated with this resolution. This
* will call back on the given PackageManager to load the label from
return label.toString().trim();
}
}
- ComponentInfo ci = activityInfo != null ? activityInfo : serviceInfo;
+ ComponentInfo ci = getComponentInfo();
ApplicationInfo ai = ci.applicationInfo;
if (labelRes != 0) {
label = pm.getText(ci.packageName, labelRes, ai);
return dr;
}
}
- ComponentInfo ci = activityInfo != null ? activityInfo : serviceInfo;
+ ComponentInfo ci = getComponentInfo();
ApplicationInfo ai = ci.applicationInfo;
if (icon != 0) {
dr = pm.getDrawable(ci.packageName, icon, ai);
*/
public final int getIconResource() {
if (icon != 0) return icon;
- if (activityInfo != null) return activityInfo.getIconResource();
- if (serviceInfo != null) return serviceInfo.getIconResource();
+ final ComponentInfo ci = getComponentInfo();
+ if (ci != null) return ci.getIconResource();
return 0;
}
} else if (serviceInfo != null) {
pw.println(prefix + "ServiceInfo:");
serviceInfo.dump(pw, prefix + " ");
+ } else if (providerInfo != null) {
+ pw.println(prefix + "ProviderInfo:");
+ providerInfo.dump(pw, prefix + " ");
}
}
public ResolveInfo(ResolveInfo orig) {
activityInfo = orig.activityInfo;
serviceInfo = orig.serviceInfo;
+ providerInfo = orig.providerInfo;
filter = orig.filter;
priority = orig.priority;
preferredOrder = orig.preferredOrder;
}
public String toString() {
- ComponentInfo ci = activityInfo != null ? activityInfo : serviceInfo;
+ final ComponentInfo ci = getComponentInfo();
StringBuilder sb = new StringBuilder(128);
sb.append("ResolveInfo{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
} else if (serviceInfo != null) {
dest.writeInt(2);
serviceInfo.writeToParcel(dest, parcelableFlags);
+ } else if (providerInfo != null) {
+ dest.writeInt(3);
+ providerInfo.writeToParcel(dest, parcelableFlags);
} else {
dest.writeInt(0);
}
};
private ResolveInfo(Parcel source) {
+ activityInfo = null;
+ serviceInfo = null;
+ providerInfo = null;
switch (source.readInt()) {
case 1:
activityInfo = ActivityInfo.CREATOR.createFromParcel(source);
- serviceInfo = null;
break;
case 2:
serviceInfo = ServiceInfo.CREATOR.createFromParcel(source);
- activityInfo = null;
+ break;
+ case 3:
+ providerInfo = ProviderInfo.CREATOR.createFromParcel(source);
break;
default:
- activityInfo = null;
- serviceInfo = null;
+ Slog.w(TAG, "Missing ComponentInfo!");
break;
}
if (source.readInt() != 0) {
* Gain factor from electrons to raw units when
* ISO=100
* </p>
+ *
+ * <b>Optional</b> - This value may be null on some devices.
+ *
+ * <b>{@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL}</b> -
+ * Present on all devices that report being FULL level hardware devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL HARDWARE_LEVEL} key.
*/
public static final Key<Rational> SENSOR_BASE_GAIN_FACTOR =
new Key<Rational>("android.sensor.baseGainFactor", Rational.class);
* values above this, it can be a mix of analog and
* digital
* </p>
+ *
+ * <b>Optional</b> - This value may be null on some devices.
+ *
+ * <b>{@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL}</b> -
+ * Present on all devices that report being FULL level hardware devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL HARDWARE_LEVEL} key.
*/
public static final Key<Integer> SENSOR_MAX_ANALOG_SENSITIVITY =
new Key<Integer>("android.sensor.maxAnalogSensitivity", int.class);
* if the format is user-visible, it must be one of android.scaler.availableFormats;
* and the size must be one of android.scaler.available[Processed|Jpeg]Sizes).</p>
*
- * <p>To change the output, the camera device must be idle. The device is considered
- * to be idle once all in-flight and pending capture requests have been processed,
- * and all output image buffers from the captures have been sent to their destination
- * Surfaces.</p>
- *
- * <p>To reach an idle state without cancelling any submitted captures, first
- * stop any repeating request/burst with {@link #stopRepeating}, and then
- * wait for the {@link StateListener#onIdle} callback to be
- * called. To idle as fast as possible, use {@link #flush} and wait for the
- * idle callback.</p>
+ * <p>When this method is called with valid Surfaces, the device will transition to the {@link
+ * StateListener#onBusy busy state}. Once configuration is complete, the device will transition
+ * into the {@link StateListener#onIdle idle state}. Capture requests using the newly-configured
+ * Surfaces may then be submitted with {@link #capture}, {@link #captureBurst}, {@link
+ * #setRepeatingRequest}, or {@link #setRepeatingBurst}.</p>
+ *
+ * <p>If this method is called while the camera device is still actively processing previously
+ * submitted captures, then the following sequence of events occurs: The device transitions to
+ * the busy state and calls the {@link StateListener#onBusy} callback. Second, if a repeating
+ * request is set it is cleared. Third, the device finishes up all in-flight and pending
+ * requests. Finally, once the device is idle, it then reconfigures its outputs, and calls the
+ * {@link StateListener#onIdle} method once it is again ready to accept capture
+ * requests. Therefore, no submitted work is discarded. To idle as fast as possible, use {@link
+ * #flush} and wait for the idle callback before calling configureOutputs. This will discard
+ * work, but reaches the new configuration sooner.</p>
*
* <p>Using larger resolution outputs, or more outputs, can result in slower
* output rate from the device.</p>
*
- * <p>Configuring the outputs with an empty or null list will transition
- * the camera into an {@link StateListener#onUnconfigured unconfigured state}.
- * </p>
+ * <p>Configuring the outputs with an empty or null list will transition the camera into an
+ * {@link StateListener#onUnconfigured unconfigured state} instead of the {@link
+ * StateListener#onIdle idle state}. </p>
*
* <p>Calling configureOutputs with the same arguments as the last call to
- * configureOutputs has no effect.</p>
+ * configureOutputs has no effect, and the {@link StateListener#onBusy busy}
+ * and {@link StateListener#onIdle idle} state transitions will happen
+ * immediately.</p>
*
* @param outputs The new set of Surfaces that should be made available as
* targets for captured image data.
* @throws IllegalStateException if the camera device is not idle, or
* if the camera device has been closed
*
+ * @see StateListener#onBusy
* @see StateListener#onIdle
+ * @see StateListener#onActive
+ * @see StateListener#onUnconfigured
* @see #stopRepeating
* @see #flush
*/
public void waitUntilIdle() throws CameraAccessException;
/**
- * Set the listener object to call when an asynchronous device event occurs,
- * such as errors or idle notifications.
- *
- * <p>The events reported here are device-wide; notifications about
- * individual capture requests or capture results are reported through
- * {@link CaptureListener}.</p>
- *
- * <p>If the camera device is idle when the listener is set, then the
- * {@link StateListener#onIdle} method will be immediately called,
- * even if the device has never been active before.
- * </p>
- *
- * @param listener the CameraDeviceListener to send device-level event
- * notifications to. Setting this to null will stop notifications.
- * @param handler the handler on which the listener should be invoked, or
- * {@code null} to use the current thread's {@link android.os.Looper looper}.
- *
- * @throws IllegalArgumentException if handler is null, the listener is
- * not null, and the calling thread has no looper
- *
- * @hide
- */
- public void setDeviceListener(StateListener listener, Handler handler);
-
- /**
* Flush all captures currently pending and in-progress as fast as
* possible.
*
public void flush() throws CameraAccessException;
/**
- * Close the connection to this camera device. After this call, all calls to
+ * Close the connection to this camera device.
+ *
+ * <p>After this call, all calls to
* the camera device interface will throw a {@link IllegalStateException},
- * except for calls to close().
+ * except for calls to close(). Once the device has fully shut down, the
+ * {@link StateListener#onClosed} callback will be called, and the camera is
+ * free to be re-opened.</p>
+ *
+ * <p>After this call, besides the final {@link StateListener#onClosed} call, no calls to the
+ * device's {@link StateListener} will occur, and any remaining submitted capture requests will
+ * not fire their {@link CaptureListener} callbacks.</p>
+ *
+ * <p>To shut down as fast as possible, call the {@link #flush} method and then {@link #close}
+ * once the flush completes. This will discard some capture requests, but results in faster
+ * shutdown.</p>
*/
@Override
public void close();
- // TODO: We should decide on the behavior of in-flight requests should be on close.
/**
* <p>A listener for tracking the progress of a {@link CaptureRequest}
* A listener for notifications about the state of a camera
* device.
*
+ * <p>A listener must be provided to the {@link CameraManager#openCamera}
+ * method to open a camera device.</p>
+ *
* <p>These events include notifications about the device becoming idle (
* allowing for {@link #configureOutputs} to be called), about device
* disconnection, and about unexpected device errors.</p>
* the {@link #capture}, {@link #captureBurst}, {@link
* #setRepeatingRequest}, or {@link #setRepeatingBurst} methods.
*
- * @see #setDeviceListener
+ * @see CameraManager#openCamera
*/
public static abstract class StateListener {
/**
* {@link #openCamera}.
*
* @param cameraId The unique identifier of the camera device to open
+ * @param listener The listener for the camera. Must not be null.
+ * @param handler The handler to call the listener on. Must not be null.
*
* @throws CameraAccessException if the camera is disabled by device policy,
* or too many camera devices are already open, or the cameraId does not match
*
* @throws SecurityException if the application does not have permission to
* access the camera
+ * @throws IllegalArgumentException if listener or handler is null.
*
* @see #getCameraIdList
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*/
- private CameraDevice openCamera(String cameraId) throws CameraAccessException {
+ private void openCameraDeviceUserAsync(String cameraId,
+ CameraDevice.StateListener listener, Handler handler)
+ throws CameraAccessException {
try {
synchronized (mLock) {
ICameraDeviceUser cameraUser;
android.hardware.camera2.impl.CameraDevice device =
- new android.hardware.camera2.impl.CameraDevice(cameraId);
+ new android.hardware.camera2.impl.CameraDevice(
+ cameraId,
+ listener,
+ handler);
BinderHolder holder = new BinderHolder();
mCameraService.connectDevice(device.getCallbacks(),
cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
// TODO: factor out listener to be non-nested, then move setter to constructor
+ // For now, calling setRemoteDevice will fire initial
+ // onOpened/onUnconfigured callbacks.
device.setRemoteDevice(cameraUser);
-
- return device;
-
}
} catch (NumberFormatException e) {
throw e.asChecked();
} catch (RemoteException e) {
// impossible
- return null;
}
}
}
}
- final CameraDevice camera = openCamera(cameraId);
- camera.setDeviceListener(listener, handler);
-
- // TODO: make truly async in the camera service
- handler.post(new Runnable() {
- @Override
- public void run() {
- listener.onOpened(camera);
- }
- });
+ openCameraDeviceUserAsync(cameraId, listener, handler);
}
/**
private CaptureRequest(CaptureRequest source) {
mSettings = new CameraMetadataNative(source.mSettings);
mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
+ mUserTag = source.mUserTag;
}
/**
* The thermal diode being queried should be inside the sensor PCB, or
* somewhere close to it.
* </p>
+ *
+ * <b>Optional</b> - This value may be null on some devices.
+ *
+ * <b>{@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL}</b> -
+ * Present on all devices that report being FULL level hardware devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL HARDWARE_LEVEL} key.
*/
public static final Key<Float> SENSOR_TEMPERATURE =
new Key<Float>("android.sensor.temperature", float.class);
* Create a new face with all fields set.
*
* <p>The id, leftEyePosition, rightEyePosition, and mouthPosition are considered optional.
- * They are only required when the {@link #CaptureResult} reports that the value of key
+ * They are only required when the {@link CaptureResult} reports that the value of key
* {@link CaptureResult#STATISTICS_FACE_DETECT_MODE} is
* {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_FULL}.
* If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and
* mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition,
* rightEyePosition, and mouthPosition may be independently null or not-null. When devices
* report the value of key {@link CaptureResult#STATISTICS_FACE_DETECT_MODE} as
- * {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_SIMPLE} in {@link #CaptureResult},
+ * {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_SIMPLE} in {@link CaptureResult},
* the face id of each face is expected to be {@value #ID_UNSUPPORTED}, the leftEyePosition,
* rightEyePosition, and mouthPositions are expected to be {@code null} for each face.</p>
*
* mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition,
* rightEyePosition, and mouthPosition may be independently null or not-null. When devices
* report the value of key {@link CaptureResult#STATISTICS_FACE_DETECT_MODE} as
- * {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_SIMPLE} in {@link #CaptureResult},
+ * {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_SIMPLE} in {@link CaptureResult},
* the face id of each face is expected to be {@value #ID_UNSUPPORTED}.</p>
*
* <p>This value will either be {@value #ID_UNSUPPORTED} or
private final Object mLock = new Object();
private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
- private StateListener mDeviceListener;
- private Handler mDeviceHandler;
+ private final StateListener mDeviceListener;
+ private final Handler mDeviceHandler;
+
+ private boolean mIdle = true;
private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
new SparseArray<CaptureListenerHolder>();
private final String mCameraId;
- public CameraDevice(String cameraId) {
+ // Runnables for all state transitions, except error, which needs the
+ // error code argument
+
+ private final Runnable mCallOnOpened = new Runnable() {
+ public void run() {
+ if (!CameraDevice.this.isClosed()) {
+ mDeviceListener.onOpened(CameraDevice.this);
+ }
+ }
+ };
+
+ private final Runnable mCallOnUnconfigured = new Runnable() {
+ public void run() {
+ if (!CameraDevice.this.isClosed()) {
+ mDeviceListener.onUnconfigured(CameraDevice.this);
+ }
+ }
+ };
+
+ private final Runnable mCallOnActive = new Runnable() {
+ public void run() {
+ if (!CameraDevice.this.isClosed()) {
+ mDeviceListener.onActive(CameraDevice.this);
+ }
+ }
+ };
+
+ private final Runnable mCallOnBusy = new Runnable() {
+ public void run() {
+ if (!CameraDevice.this.isClosed()) {
+ mDeviceListener.onBusy(CameraDevice.this);
+ }
+ }
+ };
+
+ private final Runnable mCallOnClosed = new Runnable() {
+ public void run() {
+ if (!CameraDevice.this.isClosed()) {
+ mDeviceListener.onClosed(CameraDevice.this);
+ }
+ }
+ };
+
+ private final Runnable mCallOnIdle = new Runnable() {
+ public void run() {
+ if (!CameraDevice.this.isClosed()) {
+ mDeviceListener.onIdle(CameraDevice.this);
+ }
+ }
+ };
+
+ private final Runnable mCallOnDisconnected = new Runnable() {
+ public void run() {
+ if (!CameraDevice.this.isClosed()) {
+ mDeviceListener.onDisconnected(CameraDevice.this);
+ }
+ }
+ };
+
+ public CameraDevice(String cameraId, StateListener listener, Handler handler) {
+ if (cameraId == null || listener == null || handler == null) {
+ throw new IllegalArgumentException("Null argument given");
+ }
mCameraId = cameraId;
+ mDeviceListener = listener;
+ mDeviceHandler = handler;
TAG = String.format("CameraDevice-%s-JV", mCameraId);
DEBUG = Log.isLoggable(TAG, Log.DEBUG);
}
public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
// TODO: Move from decorator to direct binder-mediated exceptions
- mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
+ synchronized(mLock) {
+ mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
+
+ mDeviceHandler.post(mCallOnOpened);
+ mDeviceHandler.post(mCallOnUnconfigured);
+ }
}
@Override
@Override
public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
+ // Treat a null input the same an empty list
+ if (outputs == null) {
+ outputs = new ArrayList<Surface>();
+ }
synchronized (mLock) {
+ checkIfCameraClosed();
+
HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
}
}
+ mDeviceHandler.post(mCallOnBusy);
+ stopRepeating();
+
try {
- // TODO: mRemoteDevice.beginConfigure
+ mRemoteDevice.waitUntilIdle();
+ // TODO: mRemoteDevice.beginConfigure
// Delete all streams first (to free up HW resources)
for (Integer streamId : deleteList) {
mRemoteDevice.deleteStream(streamId);
} catch (CameraRuntimeException e) {
if (e.getReason() == CAMERA_IN_USE) {
throw new IllegalStateException("The camera is currently busy." +
- " You must call waitUntilIdle before trying to reconfigure.");
+ " You must wait until the previous operation completes.");
}
throw e.asChecked();
// impossible
return;
}
+
+ if (outputs.size() > 0) {
+ mDeviceHandler.post(mCallOnIdle);
+ } else {
+ mDeviceHandler.post(mCallOnUnconfigured);
+ }
}
}
public CaptureRequest.Builder createCaptureRequest(int templateType)
throws CameraAccessException {
synchronized (mLock) {
+ checkIfCameraClosed();
CameraMetadataNative templatedRequest = new CameraMetadataNative();
}
synchronized (mLock) {
-
+ checkIfCameraClosed();
int requestId;
try {
mRepeatingRequestIdStack.add(requestId);
}
+ if (mIdle) {
+ mDeviceHandler.post(mCallOnActive);
+ }
+ mIdle = false;
+
return requestId;
}
}
public void stopRepeating() throws CameraAccessException {
synchronized (mLock) {
-
+ checkIfCameraClosed();
while (!mRepeatingRequestIdStack.isEmpty()) {
int requestId = mRepeatingRequestIdStack.pop();
}
@Override
- public void setDeviceListener(StateListener listener, Handler handler) {
- synchronized (mLock) {
- if (listener != null) {
- handler = checkHandler(handler);
- }
-
- mDeviceListener = listener;
- mDeviceHandler = handler;
- }
- }
-
- @Override
public void flush() throws CameraAccessException {
synchronized (mLock) {
+ checkIfCameraClosed();
+
+ mDeviceHandler.post(mCallOnBusy);
try {
mRemoteDevice.flush();
} catch (CameraRuntimeException e) {
@Override
public void close() {
-
- // TODO: every method should throw IllegalStateException after close has been called
-
synchronized (mLock) {
try {
// impossible
}
- mRemoteDevice = null;
+ if (mRemoteDevice != null) {
+ mDeviceHandler.post(mCallOnClosed);
+ }
+ mRemoteDevice = null;
}
}
@Override
public void onCameraError(final int errorCode) {
- synchronized (mLock) {
- if (CameraDevice.this.mDeviceListener == null) return;
- final StateListener listener = CameraDevice.this.mDeviceListener;
- Runnable r = null;
+ Runnable r = null;
+ if (isClosed()) return;
+
+ synchronized(mLock) {
switch (errorCode) {
case ERROR_CAMERA_DISCONNECTED:
- r = new Runnable() {
- public void run() {
- listener.onDisconnected(CameraDevice.this);
- }
- };
+ r = mCallOnDisconnected;
break;
+ default:
+ Log.e(TAG, "Unknown error from camera device: " + errorCode);
+ // no break
case ERROR_CAMERA_DEVICE:
case ERROR_CAMERA_SERVICE:
r = new Runnable() {
public void run() {
- listener.onError(CameraDevice.this, errorCode);
+ if (!CameraDevice.this.isClosed()) {
+ mDeviceListener.onError(CameraDevice.this, errorCode);
+ }
}
};
break;
- default:
- Log.e(TAG, "Unknown error from camera device: " + errorCode);
- }
- if (r != null) {
- CameraDevice.this.mDeviceHandler.post(r);
}
+ CameraDevice.this.mDeviceHandler.post(r);
}
}
@Override
public void onCameraIdle() {
+ if (isClosed()) return;
+
if (DEBUG) {
Log.d(TAG, "Camera now idle");
}
synchronized (mLock) {
- if (CameraDevice.this.mDeviceListener == null) return;
- final StateListener listener = CameraDevice.this.mDeviceListener;
- Runnable r = new Runnable() {
- public void run() {
- listener.onIdle(CameraDevice.this);
- }
- };
- CameraDevice.this.mDeviceHandler.post(r);
+ if (!CameraDevice.this.mIdle) {
+ CameraDevice.this.mDeviceHandler.post(mCallOnIdle);
+ }
+ CameraDevice.this.mIdle = true;
}
}
return;
}
+ if (isClosed()) return;
+
// Dispatch capture start notice
holder.getHandler().post(
new Runnable() {
public void run() {
- holder.getListener().onCaptureStarted(
- CameraDevice.this,
- holder.getRequest(),
- timestamp);
+ if (!CameraDevice.this.isClosed()) {
+ holder.getListener().onCaptureStarted(
+ CameraDevice.this,
+ holder.getRequest(),
+ timestamp);
+ }
}
});
}
return;
}
+ if (isClosed()) return;
+
final CaptureRequest request = holder.getRequest();
final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
new Runnable() {
@Override
public void run() {
- holder.getListener().onCaptureCompleted(
- CameraDevice.this,
- request,
- resultAsCapture);
+ if (!CameraDevice.this.isClosed()){
+ holder.getListener().onCaptureCompleted(
+ CameraDevice.this,
+ request,
+ resultAsCapture);
+ }
}
});
}
throw new IllegalStateException("CameraDevice was already closed");
}
}
+
+ private boolean isClosed() {
+ synchronized(mLock) {
+ return (mRemoteDevice == null);
+ }
+ }
}
@SuppressWarnings("unchecked")
@Override
public <T> T get(Key<T> key) {
+
+ if (key == CaptureResult.STATISTICS_FACES) {
+ /**
+ * FIXME: Workaround for HAL bug that's missing FACE_DETECT_MODE
+ */
+ Log.w(TAG, "Expected non-null android.statistics.faceDetectMode");
+ return null;
+ }
+
T value = getOverride(key);
if (value != null) {
return value;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.google.android.collect.Lists;
import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
/**
* Provides access to environment variables.
private static final String ENV_EMULATED_STORAGE_SOURCE = "EMULATED_STORAGE_SOURCE";
private static final String ENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET";
private static final String ENV_MEDIA_STORAGE = "MEDIA_STORAGE";
+ private static final String ENV_SECONDARY_STORAGE = "SECONDARY_STORAGE";
private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
/** {@hide} */
/** {@hide} */
public static class UserEnvironment {
// TODO: generalize further to create package-specific environment
- // TODO: add support for secondary external storage
/** External storage dirs, as visible to vold */
private final File[] mExternalDirsForVold;
String rawExternalStorage = System.getenv(ENV_EXTERNAL_STORAGE);
String rawEmulatedSource = System.getenv(ENV_EMULATED_STORAGE_SOURCE);
String rawEmulatedTarget = System.getenv(ENV_EMULATED_STORAGE_TARGET);
+
String rawMediaStorage = System.getenv(ENV_MEDIA_STORAGE);
if (TextUtils.isEmpty(rawMediaStorage)) {
rawMediaStorage = "/data/media";
}
+ ArrayList<File> externalForVold = Lists.newArrayList();
+ ArrayList<File> externalForApp = Lists.newArrayList();
+
if (!TextUtils.isEmpty(rawEmulatedTarget)) {
// Device has emulated storage; external storage paths should have
// userId burned into them.
final File mediaBase = new File(rawMediaStorage);
// /storage/emulated/0
- mExternalDirsForVold = new File[] { buildPath(emulatedSourceBase, rawUserId) };
- mExternalDirsForApp = new File[] { buildPath(emulatedTargetBase, rawUserId) };
+ externalForVold.add(buildPath(emulatedSourceBase, rawUserId));
+ externalForApp.add(buildPath(emulatedTargetBase, rawUserId));
// /data/media/0
mEmulatedDirForDirect = buildPath(mediaBase, rawUserId);
}
// /storage/sdcard0
- mExternalDirsForVold = new File[] { new File(rawExternalStorage) };
- mExternalDirsForApp = new File[] { new File(rawExternalStorage) };
+ externalForVold.add(new File(rawExternalStorage));
+ externalForApp.add(new File(rawExternalStorage));
// /data/media
mEmulatedDirForDirect = new File(rawMediaStorage);
}
+
+ // Splice in any secondary storage paths, but only for owner
+ final String rawSecondaryStorage = System.getenv(ENV_SECONDARY_STORAGE);
+ if (!TextUtils.isEmpty(rawSecondaryStorage) && userId == UserHandle.USER_OWNER) {
+ for (String secondaryPath : rawSecondaryStorage.split(":")) {
+ externalForVold.add(new File(secondaryPath));
+ externalForApp.add(new File(secondaryPath));
+ }
+ }
+
+ mExternalDirsForVold = externalForVold.toArray(new File[externalForVold.size()]);
+ mExternalDirsForApp = externalForApp.toArray(new File[externalForApp.size()]);
}
@Deprecated
*/
public static final MediaSize UNKNOWN_PORTRAIT =
new MediaSize("UNKNOWN_PORTRAIT", "android",
- R.string.mediasize_unknown_portrait, Integer.MAX_VALUE, 1);
+ R.string.mediasize_unknown_portrait, 1, Integer.MAX_VALUE);
/**
* Unknown media size in landscape mode.
*/
public static final MediaSize UNKNOWN_LANDSCAPE =
new MediaSize("UNKNOWN_LANDSCAPE", "android",
- R.string.mediasize_unknown_landscape, 1, Integer.MAX_VALUE);
+ R.string.mediasize_unknown_landscape, Integer.MAX_VALUE, 1);
// ISO sizes
private static final String LOG_TAG = "PrintManager";
+ private static final boolean DEBUG = false;
+
/** @hide */
public static final int APP_ID_ANY = -2;
private Handler mHandler; // Strong reference OK - cleared in finish()
+ private LayoutSpec mLastLayoutSpec;
+
+ private WriteSpec mLastWriteSpec;
+
+ private boolean mStartReqeusted;
+ private boolean mStarted;
+
+ private boolean mFinishRequested;
+ private boolean mFinished;
+
public PrintDocumentAdapterDelegate(PrintDocumentAdapter documentAdapter, Looper looper) {
mDocumentAdapter = documentAdapter;
mHandler = new MyHandler(looper);
@Override
public void start() {
- mHandler.sendEmptyMessage(MyHandler.MSG_START);
+ synchronized (mLock) {
+ // Started or finished - nothing to do.
+ if (mStartReqeusted || mFinishRequested) {
+ return;
+ }
+
+ mStartReqeusted = true;
+
+ doPendingWorkLocked();
+ }
}
@Override
public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
ILayoutResultCallback callback, Bundle metadata, int sequence) {
synchronized (mLock) {
- if (mLayoutOrWriteCancellation != null) {
- mLayoutOrWriteCancellation.cancel();
+ // Start not called or finish called - nothing to do.
+ if (!mStartReqeusted || mFinishRequested) {
+ return;
+ }
+
+ // Layout cancels write and overrides layout.
+ if (mLastWriteSpec != null) {
+ IoUtils.closeQuietly(mLastWriteSpec.fd);
+ mLastWriteSpec = null;
}
+
+ mLastLayoutSpec = new LayoutSpec();
+ mLastLayoutSpec.callback = callback;
+ mLastLayoutSpec.oldAttributes = oldAttributes;
+ mLastLayoutSpec.newAttributes = newAttributes;
+ mLastLayoutSpec.metadata = metadata;
+ mLastLayoutSpec.sequence = sequence;
+
+ // Cancel the previous cancellable operation.When the
+ // cancellation completes we will do the pending work.
+ if (cancelPreviousCancellableOperationLocked()) {
+ return;
+ }
+
+ doPendingWorkLocked();
}
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = oldAttributes;
- args.arg2 = newAttributes;
- args.arg3 = callback;
- args.arg4 = metadata;
- args.argi1 = sequence;
- mHandler.removeMessages(MyHandler.MSG_LAYOUT);
- mHandler.obtainMessage(MyHandler.MSG_LAYOUT, args).sendToTarget();
}
@Override
public void write(PageRange[] pages, ParcelFileDescriptor fd,
IWriteResultCallback callback, int sequence) {
synchronized (mLock) {
- if (mLayoutOrWriteCancellation != null) {
- mLayoutOrWriteCancellation.cancel();
+ // Start not called or finish called - nothing to do.
+ if (!mStartReqeusted || mFinishRequested) {
+ return;
+ }
+
+ // Write cancels previous writes.
+ if (mLastWriteSpec != null) {
+ IoUtils.closeQuietly(mLastWriteSpec.fd);
+ mLastWriteSpec = null;
}
+
+ mLastWriteSpec = new WriteSpec();
+ mLastWriteSpec.callback = callback;
+ mLastWriteSpec.pages = pages;
+ mLastWriteSpec.fd = fd;
+ mLastWriteSpec.sequence = sequence;
+
+ // Cancel the previous cancellable operation.When the
+ // cancellation completes we will do the pending work.
+ if (cancelPreviousCancellableOperationLocked()) {
+ return;
+ }
+
+ doPendingWorkLocked();
}
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = pages;
- args.arg2 = fd;
- args.arg3 = callback;
- args.argi1 = sequence;
- mHandler.removeMessages(MyHandler.MSG_WRITE);
- mHandler.obtainMessage(MyHandler.MSG_WRITE, args).sendToTarget();
}
@Override
public void finish() {
- mHandler.sendEmptyMessage(MyHandler.MSG_FINISH);
+ synchronized (mLock) {
+ // Start not called or finish called - nothing to do.
+ if (!mStartReqeusted || mFinishRequested) {
+ return;
+ }
+
+ mFinishRequested = true;
+
+ // When the current write or layout complete we
+ // will do the pending work.
+ if (mLastLayoutSpec != null || mLastWriteSpec != null) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Waiting for current operation");
+ }
+ return;
+ }
+
+ doPendingWorkLocked();
+ }
}
private boolean isFinished() {
private void doFinish() {
mDocumentAdapter = null;
mHandler = null;
- mLayoutOrWriteCancellation = null;
+ synchronized (mLock) {
+ mLayoutOrWriteCancellation = null;
+ }
+ }
+
+ private boolean cancelPreviousCancellableOperationLocked() {
+ if (mLayoutOrWriteCancellation != null) {
+ mLayoutOrWriteCancellation.cancel();
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Cancelling previous operation");
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void doPendingWorkLocked() {
+ if (mStartReqeusted && !mStarted) {
+ mStarted = true;
+ mHandler.sendEmptyMessage(MyHandler.MSG_START);
+ } else if (mLastLayoutSpec != null) {
+ mHandler.sendEmptyMessage(MyHandler.MSG_LAYOUT);
+ } else if (mLastWriteSpec != null) {
+ mHandler.sendEmptyMessage(MyHandler.MSG_WRITE);
+ } else if (mFinishRequested && !mFinished) {
+ mFinished = true;
+ mHandler.sendEmptyMessage(MyHandler.MSG_FINISH);
+ }
+ }
+
+ private class LayoutSpec {
+ ILayoutResultCallback callback;
+ PrintAttributes oldAttributes;
+ PrintAttributes newAttributes;
+ Bundle metadata;
+ int sequence;
+ }
+
+ private class WriteSpec {
+ IWriteResultCallback callback;
+ PageRange[] pages;
+ ParcelFileDescriptor fd;
+ int sequence;
}
private final class MyHandler extends Handler {
} break;
case MSG_LAYOUT: {
- SomeArgs args = (SomeArgs) message.obj;
- PrintAttributes oldAttributes = (PrintAttributes) args.arg1;
- PrintAttributes newAttributes = (PrintAttributes) args.arg2;
- ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3;
- Bundle metadata = (Bundle) args.arg4;
- final int sequence = args.argi1;
- args.recycle();
-
- CancellationSignal cancellation = new CancellationSignal();
+ final CancellationSignal cancellation;
+ final LayoutSpec layoutSpec;
+
synchronized (mLock) {
+ layoutSpec = mLastLayoutSpec;
+ mLastLayoutSpec = null;
+ cancellation = new CancellationSignal();
mLayoutOrWriteCancellation = cancellation;
}
- mDocumentAdapter.onLayout(oldAttributes, newAttributes, cancellation,
- new MyLayoutResultCallback(callback, sequence), metadata);
+ if (layoutSpec != null) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Performing layout");
+ }
+ mDocumentAdapter.onLayout(layoutSpec.oldAttributes,
+ layoutSpec.newAttributes, cancellation,
+ new MyLayoutResultCallback(layoutSpec.callback,
+ layoutSpec.sequence), layoutSpec.metadata);
+ }
} break;
case MSG_WRITE: {
- SomeArgs args = (SomeArgs) message.obj;
- PageRange[] pages = (PageRange[]) args.arg1;
- ParcelFileDescriptor fd = (ParcelFileDescriptor) args.arg2;
- IWriteResultCallback callback = (IWriteResultCallback) args.arg3;
- final int sequence = args.argi1;
- args.recycle();
-
- CancellationSignal cancellation = new CancellationSignal();
+ final CancellationSignal cancellation;
+ final WriteSpec writeSpec;
+
synchronized (mLock) {
+ writeSpec= mLastWriteSpec;
+ mLastWriteSpec = null;
+ cancellation = new CancellationSignal();
mLayoutOrWriteCancellation = cancellation;
}
- mDocumentAdapter.onWrite(pages, fd, cancellation,
- new MyWriteResultCallback(callback, fd, sequence));
+ if (writeSpec != null) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Performing write");
+ }
+ mDocumentAdapter.onWrite(writeSpec.pages, writeSpec.fd,
+ cancellation, new MyWriteResultCallback(writeSpec.callback,
+ writeSpec.fd, writeSpec.sequence));
+ }
} break;
case MSG_FINISH: {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Performing finish");
+ }
mDocumentAdapter.onFinish();
doFinish();
} break;
private void clearLocked() {
mLayoutOrWriteCancellation = null;
mCallback = null;
+ doPendingWorkLocked();
}
}
IoUtils.closeQuietly(mFd);
mCallback = null;
mFd = null;
+ doPendingWorkLocked();
}
}
}
}
/** {@hide} */
+ @Deprecated
public static final String META_DATA_DOCUMENT_PROVIDER = "android.content.DOCUMENT_PROVIDER";
+ /**
+ * Intent action used to identify {@link DocumentsProvider} instances.
+ */
+ public static final String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER";
+
/** {@hide} */
public static final String ACTION_MANAGE_ROOT = "android.provider.action.MANAGE_ROOT";
/** {@hide} */
SCREEN_BRIGHTNESS_MODE,
SCREEN_AUTO_BRIGHTNESS_ADJ,
VIBRATE_INPUT_DEVICES,
- MODE_RINGER, // moved to global
MODE_RINGER_STREAMS_AFFECTED,
- MUTE_STREAMS_AFFECTED,
VOLUME_VOICE,
VOLUME_SYSTEM,
VOLUME_RING,
public static final String DEVELOPMENT_FORCE_RTL = "debug.force_rtl";
/**
+ * Milliseconds after screen-off after which low battery sounds will be silenced.
+ *
+ * If zero, battery sounds will always play.
+ * Defaults to @integer/def_low_battery_sound_timeout in SettingsProvider.
+ *
+ * @hide
+ */
+ public static final String LOW_BATTERY_SOUND_TIMEOUT = "low_battery_sound_timeout";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
public static final String[] SETTINGS_TO_BACKUP = {
BUGREPORT_IN_POWER_MENU,
STAY_ON_WHILE_PLUGGED_IN,
- MODE_RINGER,
AUTO_TIME,
AUTO_TIME_ZONE,
POWER_SOUNDS_ENABLED,
* @return The magnification spec if such or null.
*/
MagnificationSpec getCompatibleMagnificationSpecForWindow(in IBinder windowToken);
+
+ /**
+ * Sets the current touch exploration state.
+ *
+ * @param enabled Whether touch exploration is enabled.
+ */
+ void setTouchExplorationEnabled(boolean enabled);
}
/**
* Masks for mPrivateFlags2, as generated by dumpFlags():
*
- * -------|-------|-------|-------|
- * PFLAG2_TEXT_ALIGNMENT_FLAGS[0]
- * PFLAG2_TEXT_DIRECTION_FLAGS[0]
- * 1 PFLAG2_DRAG_CAN_ACCEPT
- * 1 PFLAG2_DRAG_HOVERED
- * 1 PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT
- * 11 PFLAG2_TEXT_DIRECTION_MASK_SHIFT
- * 1 1 PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT
- * 11 PFLAG2_LAYOUT_DIRECTION_MASK
- * 11 1 PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT
- * 1 PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL
- * 1 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT
- * 1 1 PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT
- * 1 PFLAG2_LAYOUT_DIRECTION_RESOLVED
- * 11 PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK
- * 1 PFLAG2_TEXT_DIRECTION_FLAGS[1]
- * 1 PFLAG2_TEXT_DIRECTION_FLAGS[2]
- * 11 PFLAG2_TEXT_DIRECTION_FLAGS[3]
- * 1 PFLAG2_TEXT_DIRECTION_FLAGS[4]
- * 1 1 PFLAG2_TEXT_DIRECTION_FLAGS[5]
- * 111 PFLAG2_TEXT_DIRECTION_MASK
- * 1 PFLAG2_TEXT_DIRECTION_RESOLVED
- * 1 PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT
- * 111 PFLAG2_TEXT_DIRECTION_RESOLVED_MASK
- * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[1]
- * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[2]
- * 11 PFLAG2_TEXT_ALIGNMENT_FLAGS[3]
- * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[4]
- * 1 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[5]
- * 11 PFLAG2_TEXT_ALIGNMENT_FLAGS[6]
- * 111 PFLAG2_TEXT_ALIGNMENT_MASK
- * 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED
- * 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT
- * 111 PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK
- * 11 PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK
- * 1 PFLAG2_HAS_TRANSIENT_STATE
- * 1 PFLAG2_ACCESSIBILITY_FOCUSED
- * 1 PFLAG2_ACCESSIBILITY_STATE_CHANGED
- * 1 PFLAG2_VIEW_QUICK_REJECTED
- * 1 PFLAG2_PADDING_RESOLVED
- * -------|-------|-------|-------|
+ * |-------|-------|-------|-------|
+ * 1 PFLAG2_DRAG_CAN_ACCEPT
+ * 1 PFLAG2_DRAG_HOVERED
+ * 11 PFLAG2_LAYOUT_DIRECTION_MASK
+ * 1 PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL
+ * 1 PFLAG2_LAYOUT_DIRECTION_RESOLVED
+ * 11 PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK
+ * 1 PFLAG2_TEXT_DIRECTION_FLAGS[1]
+ * 1 PFLAG2_TEXT_DIRECTION_FLAGS[2]
+ * 11 PFLAG2_TEXT_DIRECTION_FLAGS[3]
+ * 1 PFLAG2_TEXT_DIRECTION_FLAGS[4]
+ * 1 1 PFLAG2_TEXT_DIRECTION_FLAGS[5]
+ * 111 PFLAG2_TEXT_DIRECTION_MASK
+ * 1 PFLAG2_TEXT_DIRECTION_RESOLVED
+ * 1 PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT
+ * 111 PFLAG2_TEXT_DIRECTION_RESOLVED_MASK
+ * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[1]
+ * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[2]
+ * 11 PFLAG2_TEXT_ALIGNMENT_FLAGS[3]
+ * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[4]
+ * 1 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[5]
+ * 11 PFLAG2_TEXT_ALIGNMENT_FLAGS[6]
+ * 111 PFLAG2_TEXT_ALIGNMENT_MASK
+ * 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED
+ * 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT
+ * 111 PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK
+ * 111 PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK
+ * 11 PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK
+ * 1 PFLAG2_ACCESSIBILITY_FOCUSED
+ * 1 PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED
+ * 1 PFLAG2_VIEW_QUICK_REJECTED
+ * 1 PFLAG2_PADDING_RESOLVED
+ * 1 PFLAG2_DRAWABLE_RESOLVED
+ * 1 PFLAG2_HAS_TRANSIENT_STATE
+ * |-------|-------|-------|-------|
*/
/**
static final int LAYOUT_DIRECTION_RESOLVED_DEFAULT = LAYOUT_DIRECTION_LTR;
/**
- * Indicates that the view is tracking some sort of transient state
- * that the app should not need to be aware of, but that the framework
- * should take special care to preserve.
- *
- * @hide
- */
- static final int PFLAG2_HAS_TRANSIENT_STATE = 0x1 << 22;
-
- /**
* Text direction is inherited thru {@link ViewGroup}
*/
public static final int TEXT_DIRECTION_INHERIT = 0;
static final int PFLAG2_DRAWABLE_RESOLVED = 0x40000000;
/**
+ * Indicates that the view is tracking some sort of transient state
+ * that the app should not need to be aware of, but that the framework
+ * should take special care to preserve.
+ */
+ static final int PFLAG2_HAS_TRANSIENT_STATE = 0x80000000;
+
+ /**
* Group of bits indicating that RTL properties resolution is done.
*/
static final int ALL_RTL_PROPERTIES_RESOLVED = PFLAG2_LAYOUT_DIRECTION_RESOLVED |
/**
* Flag for {@link #setSystemUiVisibility(int)}: View would like to remain interactive when
+ * hiding the navigation bar with {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}. If this flag is
+ * not set, {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION} will be force cleared by the system on any
+ * user interaction.
+ * <p>Since this flag is a modifier for {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, it only
+ * has an effect when used in combination with that flag.</p>
+ */
+ public static final int SYSTEM_UI_FLAG_IMMERSIVE = 0x00000800;
+
+ /**
+ * Flag for {@link #setSystemUiVisibility(int)}: View would like to remain interactive when
* hiding the status bar with {@link #SYSTEM_UI_FLAG_FULLSCREEN} and/or hiding the navigation
* bar with {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}. Use this flag to create an immersive
* experience while also hiding the system bars. If this flag is not set,
* {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, it only has an effect when used in combination
* with one or both of those flags.</p>
*/
- public static final int SYSTEM_UI_FLAG_IMMERSIVE = 0x00000800;
+ public static final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 0x00001000;
/**
* @deprecated Use {@link #SYSTEM_UI_FLAG_LOW_PROFILE} instead.
* @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO
*/
public void setImportantForAccessibility(int mode) {
- final boolean oldIncludeForAccessibility = includeForAccessibility();
- if (mode != getImportantForAccessibility()) {
+ final int oldMode = getImportantForAccessibility();
+ if (mode != oldMode) {
+ // If we're moving between AUTO and another state, we might not need
+ // to send a subtree changed notification. We'll store the computed
+ // importance, since we'll need to check it later to make sure.
+ final boolean maySkipNotify = oldMode == IMPORTANT_FOR_ACCESSIBILITY_AUTO
+ || mode == IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+ final boolean oldIncludeForAccessibility = maySkipNotify && includeForAccessibility();
mPrivateFlags2 &= ~PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK;
mPrivateFlags2 |= (mode << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT)
& PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK;
- if (oldIncludeForAccessibility != includeForAccessibility()) {
+ if (!maySkipNotify || oldIncludeForAccessibility != includeForAccessibility()) {
notifySubtreeAccessibilityStateChangedIfNeeded();
} else {
notifyViewAccessibilityStateChangedIfNeeded(
* @param visibility Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE},
* {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, {@link #SYSTEM_UI_FLAG_FULLSCREEN},
* {@link #SYSTEM_UI_FLAG_LAYOUT_STABLE}, {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION},
- * {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}, and {@link #SYSTEM_UI_FLAG_IMMERSIVE}.
+ * {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}, {@link #SYSTEM_UI_FLAG_IMMERSIVE},
+ * and {@link #SYSTEM_UI_FLAG_IMMERSIVE_STICKY}.
*/
public void setSystemUiVisibility(int visibility) {
if (visibility != mSystemUiVisibility) {
* @return Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE},
* {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, {@link #SYSTEM_UI_FLAG_FULLSCREEN},
* {@link #SYSTEM_UI_FLAG_LAYOUT_STABLE}, {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION},
- * {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}, and {@link #SYSTEM_UI_FLAG_IMMERSIVE}.
+ * {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}, {@link #SYSTEM_UI_FLAG_IMMERSIVE},
+ * and {@link #SYSTEM_UI_FLAG_IMMERSIVE_STICKY}.
*/
public int getSystemUiVisibility() {
return mSystemUiVisibility;
* @return True if the window is a top level one.
*/
public boolean isTopLevelWindow(int windowType);
+
+ /**
+ * Sets the current touch exploration state.
+ *
+ * @param enabled Whether touch exploration is enabled.
+ */
+ public void setTouchExplorationEnabled(boolean enabled);
}
/**
* Sets the performed action that triggered this event.
+ * <p>
+ * Valid actions are defined in {@link AccessibilityNodeInfo}:
+ * <ul>
+ * <li>{@link AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS}
+ * <li>{@link AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS}
+ * <li>{@link AccessibilityNodeInfo#ACTION_CLEAR_FOCUS}
+ * <li>{@link AccessibilityNodeInfo#ACTION_CLEAR_SELECTION}
+ * <li>{@link AccessibilityNodeInfo#ACTION_CLICK}
+ * <li>etc.
+ * </ul>
*
* @param action The action.
- *
* @throws IllegalStateException If called from an AccessibilityService.
+ * @see AccessibilityNodeInfo#performAction(int)
*/
public void setAction(int action) {
enforceNotSealed();
refreshCachedNode(event.getSourceNodeId());
} break;
case AccessibilityEvent.TYPE_VIEW_SCROLLED: {
- clearSubTreeLocked(event.getSourceNodeId());
+ synchronized (mLock) {
+ clearSubTreeLocked(event.getSourceNodeId());
+ }
} break;
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
synchronized (mLock) {
* @hide
*/
public final class WebViewFactory {
- private static final boolean DEFAULT_TO_EXPERIMENTAL_WEBVIEW = true;
- // REMEMBER: property names must be <= 31 chars total.
- private static final String EXPERIMENTAL_PROPERTY_DEFAULT_OFF = "persist.sys.webview.exp";
- private static final String EXPERIMENTAL_PROPERTY_DEFAULT_ON =
- "persist.sys.webview." + Build.ID;
-
- // Modify the persisted property name when the new webview is on-by-default, so that any user
- // setting override only lives as long as that build.
- private static final String LONG_PROPERTY_NAME = DEFAULT_TO_EXPERIMENTAL_WEBVIEW ?
- EXPERIMENTAL_PROPERTY_DEFAULT_ON : EXPERIMENTAL_PROPERTY_DEFAULT_OFF;
- private static final String WEBVIEW_EXPERIMENTAL_PROPERTY =
- LONG_PROPERTY_NAME.length() > SystemProperties.PROP_NAME_MAX ?
- LONG_PROPERTY_NAME.substring(0, SystemProperties.PROP_NAME_MAX) : LONG_PROPERTY_NAME;
private static final String FORCE_PROVIDER_PROPERTY = "webview.force_provider";
private static final String FORCE_PROVIDER_PROPERTY_VALUE_CHROMIUM = "chromium";
private static final Object sProviderLock = new Object();
public static boolean isExperimentalWebViewAvailable() {
- try {
- // Pass false so we don't initialize the class at this point, as this will be wasted if
- // it's not enabled.
- Class.forName(CHROMIUM_WEBVIEW_FACTORY, false, WebViewFactory.class.getClassLoader());
- return true;
- } catch (ClassNotFoundException e) {
- return false;
- }
+ // TODO: Remove callers of this method then remove it.
+ return false; // Hide the toggle in Developer Settings.
}
/** @hide */
public static void setUseExperimentalWebView(boolean enable) {
- SystemProperties.set(WEBVIEW_EXPERIMENTAL_PROPERTY, enable ? "true" : "false");
- Log.i(LOGTAG, "Use Experimental WebView changed: "
- + SystemProperties.get(WebViewFactory.WEBVIEW_EXPERIMENTAL_PROPERTY, ""));
+ // TODO: Remove callers of this method then remove it.
}
/** @hide */
public static boolean useExperimentalWebView() {
- return SystemProperties.getBoolean(WEBVIEW_EXPERIMENTAL_PROPERTY,
- DEFAULT_TO_EXPERIMENTAL_WEBVIEW);
+ // TODO: Remove callers of this method then remove it.
+ return isChromiumWebViewEnabled();
}
/** @hide */
public static boolean isUseExperimentalWebViewSet() {
- return !SystemProperties.get(WEBVIEW_EXPERIMENTAL_PROPERTY).isEmpty();
+ // TODO: Remove callers of this method then remove it.
+ return false; // User has not modifed Developer Settings
}
static WebViewFactoryProvider getProvider() {
// We allow a system property to specify that we should use the experimental Chromium powered
// WebView. This enables us to switch between implementations at runtime.
- private static boolean isExperimentalWebViewEnabled() {
- if (!isExperimentalWebViewAvailable()) return false;
+ private static boolean isChromiumWebViewEnabled() {
String forceProviderName = SystemProperties.get(FORCE_PROVIDER_PROPERTY);
- if (forceProviderName.isEmpty()) return useExperimentalWebView();
+ if (forceProviderName.isEmpty()) return true;
Log.i(LOGTAG, String.format("Provider overridden by property: %s=%s",
FORCE_PROVIDER_PROPERTY, forceProviderName));
if (forceProviderName.equals(FORCE_PROVIDER_PROPERTY_VALUE_CHROMIUM)) return true;
if (forceProviderName.equals(FORCE_PROVIDER_PROPERTY_VALUE_CLASSIC)) return false;
Log.e(LOGTAG, String.format("Unrecognized provider: %s", forceProviderName));
- return useExperimentalWebView();
+ return true;
}
private static Class<WebViewFactoryProvider> getFactoryClass() throws ClassNotFoundException {
- if (isExperimentalWebViewEnabled()) {
+ if (isChromiumWebViewEnabled()) {
return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY);
} else {
return (Class<WebViewFactoryProvider>) Class.forName(DEFAULT_WEBVIEW_FACTORY);
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.RectF;
+import android.graphics.Xfermode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
// these are applied to the drawable
private ColorFilter mColorFilter;
+ private Xfermode mXfermode;
private int mAlpha = 255;
private int mViewAlphaScale = 256;
private boolean mColorMod = false;
}
/**
+ * @hide Candidate for future API inclusion
+ */
+ public final void setXfermode(Xfermode mode) {
+ if (mXfermode != mode) {
+ mXfermode = mode;
+ mColorMod = true;
+ applyColorMod();
+ invalidate();
+ }
+ }
+
+ /**
* Returns the active color filter for this ImageView.
*
* @return the active color filter for this ImageView
if (mDrawable != null && mColorMod) {
mDrawable = mDrawable.mutate();
mDrawable.setColorFilter(mColorFilter);
+ mDrawable.setXfermode(mXfermode);
mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8);
}
}
// Restore default.
runtime.setTargetHeapUtilization(defaultUtilization);
+ // Fill in dex caches with classes, fields, and methods brought in by preloading.
+ runtime.preloadDexCaches();
+
Debug.stopAllocCounting();
// Bring back root. We'll need it later.
}
SkAutoTUnref<SkData> data(SkData::NewFromFD(descriptor));
+ if (data.get() == NULL) {
+ return nullObjectReturn("NewFromFD failed in nativeDecodeFileDescriptor");
+ }
SkAutoTUnref<SkMemoryStream> stream(new SkMemoryStream(data));
/* Allow purgeable iff we own the FD, i.e., in the puregeable and
<item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_light" />
<item android:state_window_focused="false" android:state_enabled="false" android:drawable="@drawable/textfield_disabled_holo_light" />
<item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_activated_holo_light" />
- <iten android:state_enabled="true" android:state_activated="true" android:drawable="@drawable/textfield_focused_holo_light" />
+ <item android:state_enabled="true" android:state_activated="true" android:drawable="@drawable/textfield_focused_holo_light" />
<item android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_light" />
<item android:state_focused="true" android:drawable="@drawable/textfield_disabled_focused_holo_light" />
<item android:drawable="@drawable/textfield_disabled_holo_light" />
mLastDirection = null;
if (rects != null) rects.setEmpty();
}
+ // We promised not to change this, so preserve it around the native
+ // call, which does now reset fill type.
+ final FillType fillType = getFillType();
native_reset(mNativePath);
+ setFillType(fillType);
}
/**
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Shader;
+import android.graphics.Xfermode;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.LayoutDirection;
}
/**
+ * @hide Candidate for future API inclusion
+ */
+ public void setXfermode(Xfermode xfermode) {
+ mBitmapState.mPaint.setXfermode(xfermode);
+ invalidateSelf();
+ }
+
+ /**
* A mutable BitmapDrawable still shares its Bitmap with any other Drawable
* that comes from the same resource.
*
package android.graphics.drawable;
import android.graphics.Insets;
+import android.graphics.Xfermode;
import android.os.Trace;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
public abstract void setColorFilter(ColorFilter cf);
/**
+ * @hide Consider for future API inclusion
+ */
+ public void setXfermode(Xfermode mode) {
+ // Base implementation drops it on the floor for compatibility. Whee!
+ // TODO: For this to be included in the API proper, all framework drawables need impls.
+ // For right now only BitmapDrawable has it.
+ }
+
+ /**
* Specify a color and porterduff mode to be the colorfilter for this
* drawable.
*/
--- /dev/null
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+
+public class PathTest extends TestCase {
+
+ @SmallTest
+ public void testResetPreservesFillType() throws Exception {
+ Path path = new Path();
+
+ final Path.FillType defaultFillType = path.getFillType();
+ final Path.FillType fillType = Path.FillType.INVERSE_EVEN_ODD;
+ assertFalse(fillType.equals(defaultFillType)); // Sanity check for the test itself.
+
+ path.setFillType(fillType);
+ path.reset();
+ assertEquals(path.getFillType(), fillType);
+ }
+}
// tell the RCCs about the change for this RCD
enableRemoteControlDisplayForClient_syncRcStack(
di.mRcDisplay, di.mEnabled);
+ // when enabling, refresh the information on the display
+ if (di.mEnabled) {
+ sendMsg(mEventHandler, MSG_RCDISPLAY_INIT_INFO, SENDMSG_QUEUE,
+ di.mArtworkExpectedWidth /*arg1*/,
+ di.mArtworkExpectedHeight/*arg2*/,
+ di.mRcDisplay /*obj*/, 0/*delay*/);
+ }
} catch (RemoteException e) {
Log.e(TAG, "Error en/disabling RCD: ", e);
}
import android.util.Log;
import android.view.KeyEvent;
+import java.lang.ref.WeakReference;
+
/**
* The RemoteController class is used to control media playback, display and update media metadata
* and playback status, published by applications using the {@link RemoteControlClient} class.
}
mOnClientUpdateListener = updateListener;
mContext = context;
- mRcd = new RcDisplay();
+ mRcd = new RcDisplay(this);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
if (ActivityManager.isLowRamDeviceStatic()) {
//==================================================
// Implementation of IRemoteControlDisplay interface
- private class RcDisplay extends IRemoteControlDisplay.Stub {
+ private static class RcDisplay extends IRemoteControlDisplay.Stub {
+ private final WeakReference<RemoteController> mController;
+
+ RcDisplay(RemoteController rc) {
+ mController = new WeakReference<RemoteController>(rc);
+ }
public void setCurrentClientId(int genId, PendingIntent clientMediaIntent,
boolean clearing) {
+ final RemoteController rc = mController.get();
+ if (rc == null) {
+ return;
+ }
boolean isNew = false;
synchronized(mGenLock) {
- if (mClientGenerationIdCurrent != genId) {
- mClientGenerationIdCurrent = genId;
+ if (rc.mClientGenerationIdCurrent != genId) {
+ rc.mClientGenerationIdCurrent = genId;
isNew = true;
}
}
if (clientMediaIntent != null) {
- sendMsg(mEventHandler, MSG_NEW_PENDING_INTENT, SENDMSG_REPLACE,
+ sendMsg(rc.mEventHandler, MSG_NEW_PENDING_INTENT, SENDMSG_REPLACE,
genId /*arg1*/, 0, clientMediaIntent /*obj*/, 0 /*delay*/);
}
if (isNew || clearing) {
- sendMsg(mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE,
+ sendMsg(rc.mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE,
genId /*arg1*/, clearing ? 1 : 0, null /*obj*/, 0 /*delay*/);
}
}
public void setEnabled(boolean enabled) {
- sendMsg(mEventHandler, MSG_DISPLAY_ENABLE, SENDMSG_REPLACE,
+ final RemoteController rc = mController.get();
+ if (rc == null) {
+ return;
+ }
+ sendMsg(rc.mEventHandler, MSG_DISPLAY_ENABLE, SENDMSG_REPLACE,
enabled ? 1 : 0 /*arg1*/, 0, null /*obj*/, 0 /*delay*/);
}
public void setPlaybackState(int genId, int state,
long stateChangeTimeMs, long currentPosMs, float speed) {
+ final RemoteController rc = mController.get();
+ if (rc == null) {
+ return;
+ }
if (DEBUG) {
Log.d(TAG, "> new playback state: genId="+genId
+ " state="+ state
}
synchronized(mGenLock) {
- if (mClientGenerationIdCurrent != genId) {
+ if (rc.mClientGenerationIdCurrent != genId) {
return;
}
}
final PlaybackInfo playbackInfo =
new PlaybackInfo(state, stateChangeTimeMs, currentPosMs, speed);
- sendMsg(mEventHandler, MSG_NEW_PLAYBACK_INFO, SENDMSG_REPLACE,
+ sendMsg(rc.mEventHandler, MSG_NEW_PLAYBACK_INFO, SENDMSG_REPLACE,
genId /*arg1*/, 0, playbackInfo /*obj*/, 0 /*delay*/);
}
public void setTransportControlInfo(int genId, int transportControlFlags,
int posCapabilities) {
+ final RemoteController rc = mController.get();
+ if (rc == null) {
+ return;
+ }
synchronized(mGenLock) {
- if (mClientGenerationIdCurrent != genId) {
+ if (rc.mClientGenerationIdCurrent != genId) {
return;
}
}
- sendMsg(mEventHandler, MSG_NEW_TRANSPORT_INFO, SENDMSG_REPLACE,
+ sendMsg(rc.mEventHandler, MSG_NEW_TRANSPORT_INFO, SENDMSG_REPLACE,
genId /*arg1*/, transportControlFlags /*arg2*/,
null /*obj*/, 0 /*delay*/);
}
public void setMetadata(int genId, Bundle metadata) {
+ final RemoteController rc = mController.get();
+ if (rc == null) {
+ return;
+ }
if (DEBUG) { Log.e(TAG, "setMetadata("+genId+")"); }
if (metadata == null) {
return;
}
synchronized(mGenLock) {
- if (mClientGenerationIdCurrent != genId) {
+ if (rc.mClientGenerationIdCurrent != genId) {
return;
}
}
- sendMsg(mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
+ sendMsg(rc.mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
genId /*arg1*/, 0 /*arg2*/,
metadata /*obj*/, 0 /*delay*/);
}
public void setArtwork(int genId, Bitmap artwork) {
+ final RemoteController rc = mController.get();
+ if (rc == null) {
+ return;
+ }
if (DEBUG) { Log.v(TAG, "setArtwork("+genId+")"); }
synchronized(mGenLock) {
- if (mClientGenerationIdCurrent != genId) {
+ if (rc.mClientGenerationIdCurrent != genId) {
return;
}
}
Bundle metadata = new Bundle(1);
metadata.putParcelable(String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK), artwork);
- sendMsg(mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
+ sendMsg(rc.mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
genId /*arg1*/, 0 /*arg2*/,
metadata /*obj*/, 0 /*delay*/);
}
public void setAllMetadata(int genId, Bundle metadata, Bitmap artwork) {
+ final RemoteController rc = mController.get();
+ if (rc == null) {
+ return;
+ }
if (DEBUG) { Log.e(TAG, "setAllMetadata("+genId+")"); }
if ((metadata == null) && (artwork == null)) {
return;
}
synchronized(mGenLock) {
- if (mClientGenerationIdCurrent != genId) {
+ if (rc.mClientGenerationIdCurrent != genId) {
return;
}
}
metadata.putParcelable(String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK),
artwork);
}
- sendMsg(mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
+ sendMsg(rc.mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
genId /*arg1*/, 0 /*arg2*/,
metadata /*obj*/, 0 /*delay*/);
}
}
private void onDisplayEnable(boolean enabled) {
+ final OnClientUpdateListener l;
synchronized(mInfoLock) {
mEnabled = enabled;
+ l = this.mOnClientUpdateListener;
+ }
+ if (!enabled) {
+ // when disabling, reset all info sent to the user
+ final int genId;
+ synchronized (mGenLock) {
+ genId = mClientGenerationIdCurrent;
+ }
+ // send "stopped" state, happened "now", playback position is 0, speed 0.0f
+ final PlaybackInfo pi = new PlaybackInfo(RemoteControlClient.PLAYSTATE_STOPPED,
+ SystemClock.elapsedRealtime() /*stateChangeTimeMs*/,
+ 0 /*currentPosMs*/, 0.0f /*speed*/);
+ sendMsg(mEventHandler, MSG_NEW_PLAYBACK_INFO, SENDMSG_REPLACE,
+ genId /*arg1*/, 0 /*arg2, ignored*/, pi /*obj*/, 0 /*delay*/);
+ // send "blank" transport control info: no controls are supported
+ sendMsg(mEventHandler, MSG_NEW_TRANSPORT_INFO, SENDMSG_REPLACE,
+ genId /*arg1*/, 0 /*arg2, no flags*/,
+ null /*obj, ignored*/, 0 /*delay*/);
+ // send dummy metadata with empty string for title and artist, duration of 0
+ Bundle metadata = new Bundle(3);
+ metadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_TITLE), "");
+ metadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_ARTIST), "");
+ metadata.putLong(String.valueOf(MediaMetadataRetriever.METADATA_KEY_DURATION), 0);
+ sendMsg(mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
+ genId /*arg1*/, 0 /*arg2, ignored*/, metadata /*obj*/, 0 /*delay*/);
}
}
final DocumentsActivity activity = (DocumentsActivity) getActivity();
final DocumentInfo cwd = activity.getCurrentDirectory();
- new CreateDirectoryTask(displayName).executeOnExecutor(
+ new CreateDirectoryTask(activity, cwd, displayName).executeOnExecutor(
ProviderExecutor.forAuthority(cwd.authority));
}
});
}
private class CreateDirectoryTask extends AsyncTask<Void, Void, DocumentInfo> {
+ private final DocumentsActivity mActivity;
+ private final DocumentInfo mCwd;
private final String mDisplayName;
- public CreateDirectoryTask(String displayName) {
+ public CreateDirectoryTask(
+ DocumentsActivity activity, DocumentInfo cwd, String displayName) {
+ mActivity = activity;
+ mCwd = cwd;
mDisplayName = displayName;
}
@Override
protected DocumentInfo doInBackground(Void... params) {
- final DocumentsActivity activity = (DocumentsActivity) getActivity();
- final ContentResolver resolver = activity.getContentResolver();
-
- final DocumentInfo cwd = activity.getCurrentDirectory();
-
+ final ContentResolver resolver = mActivity.getContentResolver();
ContentProviderClient client = null;
try {
client = DocumentsApplication.acquireUnstableProviderOrThrow(
- resolver, cwd.derivedUri.getAuthority());
+ resolver, mCwd.derivedUri.getAuthority());
final Uri childUri = DocumentsContract.createDocument(
- client, cwd.derivedUri, Document.MIME_TYPE_DIR, mDisplayName);
+ client, mCwd.derivedUri, Document.MIME_TYPE_DIR, mDisplayName);
return DocumentInfo.fromUri(resolver, childUri);
} catch (Exception e) {
Log.w(TAG, "Failed to create directory", e);
@Override
protected void onPostExecute(DocumentInfo result) {
- final DocumentsActivity activity = (DocumentsActivity) getActivity();
if (result != null) {
// Navigate into newly created child
- activity.onDocumentPicked(result);
+ mActivity.onDocumentPicked(result);
} else {
- Toast.makeText(activity, R.string.create_error, Toast.LENGTH_SHORT).show();
+ Toast.makeText(mActivity, R.string.create_error, Toast.LENGTH_SHORT).show();
}
}
}
updateDisplayState();
+ // When launched into empty recents, show drawer
+ if (mType == TYPE_RECENT_OPEN && mAdapter.isEmpty() && !state.stackTouched) {
+ ((DocumentsActivity) context).setRootsDrawerOpen(true);
+ }
+
// Restore any previous instance state
final SparseArray<Parcelable> container = state.dirState.remove(mStateKey);
if (container != null && !getArguments().getBoolean(EXTRA_IGNORE_STATE, false)) {
public void onLoadFinished(
Loader<List<DocumentStack>> loader, List<DocumentStack> data) {
mAdapter.swapStacks(data);
+
+ // When launched into empty recents, show drawer
+ if (mAdapter.isEmpty() && !state.stackTouched) {
+ ((DocumentsActivity) context).setRootsDrawerOpen(true);
+ }
}
@Override
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
private class UpdateTask extends AsyncTask<Void, Void, Void> {
private final String mFilterPackage;
+ private final Multimap<String, RootInfo> mTaskRoots = ArrayListMultimap.create();
+ private final HashSet<String> mTaskStoppedAuthorities = Sets.newHashSet();
+
/**
* Update all roots.
*/
protected Void doInBackground(Void... params) {
final long start = SystemClock.elapsedRealtime();
- final Multimap<String, RootInfo> roots = ArrayListMultimap.create();
- final HashSet<String> stoppedAuthorities = Sets.newHashSet();
-
- roots.put(mRecentsRoot.authority, mRecentsRoot);
+ mTaskRoots.put(mRecentsRoot.authority, mRecentsRoot);
final ContentResolver resolver = mContext.getContentResolver();
final PackageManager pm = mContext.getPackageManager();
- final List<ProviderInfo> providers = pm.queryContentProviders(
+
+ // Pick up provider with action string
+ final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
+ final List<ResolveInfo> providers = pm.queryIntentContentProviders(intent, 0);
+ for (ResolveInfo info : providers) {
+ handleDocumentsProvider(info.providerInfo);
+ }
+
+ // Pick up legacy providers
+ final List<ProviderInfo> legacyProviders = pm.queryContentProviders(
null, -1, PackageManager.GET_META_DATA);
- for (ProviderInfo info : providers) {
+ for (ProviderInfo info : legacyProviders) {
if (info.metaData != null && info.metaData.containsKey(
DocumentsContract.META_DATA_DOCUMENT_PROVIDER)) {
- // Ignore stopped packages for now; we might query them
- // later during UI interaction.
- if ((info.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) {
- if (LOGD) Log.d(TAG, "Ignoring stopped authority " + info.authority);
- stoppedAuthorities.add(info.authority);
- continue;
- }
-
- // Try using cached roots if filtering
- boolean cacheHit = false;
- if (mFilterPackage != null && !mFilterPackage.equals(info.packageName)) {
- synchronized (mLock) {
- if (roots.putAll(info.authority, mRoots.get(info.authority))) {
- if (LOGD) Log.d(TAG, "Used cached roots for " + info.authority);
- cacheHit = true;
- }
- }
- }
-
- // Cache miss, or loading everything
- if (!cacheHit) {
- roots.putAll(
- info.authority, loadRootsForAuthority(resolver, info.authority));
- }
+ handleDocumentsProvider(info);
}
}
final long delta = SystemClock.elapsedRealtime() - start;
- Log.d(TAG, "Update found " + roots.size() + " roots in " + delta + "ms");
+ Log.d(TAG, "Update found " + mTaskRoots.size() + " roots in " + delta + "ms");
synchronized (mLock) {
- mStoppedAuthorities = stoppedAuthorities;
- mRoots = roots;
+ mRoots = mTaskRoots;
+ mStoppedAuthorities = mTaskStoppedAuthorities;
}
mFirstLoad.countDown();
return null;
}
+
+ private void handleDocumentsProvider(ProviderInfo info) {
+ // Ignore stopped packages for now; we might query them
+ // later during UI interaction.
+ if ((info.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) {
+ if (LOGD) Log.d(TAG, "Ignoring stopped authority " + info.authority);
+ mTaskStoppedAuthorities.add(info.authority);
+ return;
+ }
+
+ // Try using cached roots if filtering
+ boolean cacheHit = false;
+ if (mFilterPackage != null && !mFilterPackage.equals(info.packageName)) {
+ synchronized (mLock) {
+ if (mTaskRoots.putAll(info.authority, mRoots.get(info.authority))) {
+ if (LOGD) Log.d(TAG, "Used cached roots for " + info.authority);
+ cacheHit = true;
+ }
+ }
+ }
+
+ // Cache miss, or loading everything
+ if (!cacheHit) {
+ mTaskRoots.putAll(info.authority,
+ loadRootsForAuthority(mContext.getContentResolver(), info.authority));
+ }
+ }
}
/**
final RootInfo root = ((DocumentsActivity) getActivity()).getCurrentRoot();
for (int i = 0; i < mAdapter.getCount(); i++) {
final Object item = mAdapter.getItem(i);
- if (Objects.equal(item, root)) {
- mList.setItemChecked(i, true);
- return;
+ if (item instanceof RootItem) {
+ final RootInfo testRoot = ((RootItem) item).root;
+ if (Objects.equal(testRoot, root)) {
+ mList.setItemChecked(i, true);
+ return;
+ }
}
}
}
android:grantUriPermissions="true"
android:exported="true"
android:permission="android.permission.MANAGE_DOCUMENTS">
- <meta-data
- android:name="android.content.DOCUMENT_PROVIDER"
- android:value="true" />
+ <intent-filter>
+ <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
+ </intent-filter>
</provider>
<!-- TODO: find a better place for tests to live -->
android:exported="true"
android:permission="android.permission.MANAGE_DOCUMENTS"
android:enabled="false">
- <meta-data
- android:name="android.content.DOCUMENT_PROVIDER"
- android:value="true" />
+ <intent-filter>
+ <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
+ </intent-filter>
</provider>
</application>
</manifest>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false" android:drawable="@drawable/scrubber_control_disabled_holo" />
+ <item android:state_pressed="true" android:drawable="@drawable/scrubber_control_pressed_holo" />
+ <item android:state_selected="true" android:drawable="@drawable/scrubber_control_focused_holo" />
+ <item android:drawable="@drawable/scrubber_control_normal_holo" />
+</selector>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background"
+ android:drawable="@drawable/scrubber_track_holo_light" />
+ <item android:id="@android:id/secondaryProgress">
+ <scale android:scaleWidth="100%"
+ android:drawable="@drawable/scrubber_secondary_holo" />
+ </item>
+ <item android:id="@android:id/progress">
+ <scale android:scaleWidth="100%"
+ android:drawable="@drawable/scrubber_primary_holo" />
+ </item>
+</layer-list>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/clock_white"
- android:format12Hour="@string/abbrev_wday_month_day_no_year"
- android:format24Hour="@string/abbrev_wday_month_day_no_year"
style="@style/widget_label"
android:gravity="center"
/>
<SeekBar
android:id="@+id/transient_seek_bar"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ style="@style/Widget.TransportControl.SeekBar" />
<TextView
android:id="@+id/transient_seek_time_elapsed"
android:layout_width="wrap_content"
<string name="keyguard_accessibility_transport_stop_description" msgid="7656358482980912216">"Кнопка \"Зупинити\""</string>
<string name="keyguard_accessibility_transport_thumbs_up_description" msgid="4535938129663903194">"Подобається"</string>
<string name="keyguard_accessibility_transport_thumbs_down_description" msgid="8101433677192177861">"Не подобається"</string>
- <string name="keyguard_accessibility_transport_heart_description" msgid="2336943232474689887">"СеÑ\80Ñ\86е"</string>
+ <string name="keyguard_accessibility_transport_heart_description" msgid="2336943232474689887">"РейÑ\82инг"</string>
<string name="keyguard_accessibility_show_bouncer" msgid="5425837272418176176">"Розблокуйте, щоб продовжити"</string>
<string name="keyguard_accessibility_hide_bouncer" msgid="7896992171878309358">"Запуск скасовано"</string>
<string name="keyguard_accessibility_delete_widget_start" msgid="4096550552634391451">"Відпустіть <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>, щоб видалити."</string>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- String matching the lock screen format for displaying the date. -->
- <string name="abbrev_wday_month_day_no_year">EEE, MMMM d</string>
- <!-- Format for describing the date, for accessibility. -->
- <string name="full_wday_month_day_no_year">EEEE, MMMM d</string>
+ <string name="abbrev_wday_month_day_no_year">EEEMMMMd</string>
</resources>
<item name="android:textSize">@dimen/widget_big_font_size</item>
</style>
+ <style name="Widget.TransportControl.SeekBar" parent="@android:style/Widget.Holo.SeekBar">
+ <item name="android:indeterminateOnly">false</item>
+ <item name="android:progressDrawable">@drawable/scrubber_progress_horizontal_holo_light</item>
+ <item name="android:indeterminateDrawable">@drawable/scrubber_progress_horizontal_holo_light</item>
+ <item name="android:minHeight">13dip</item>
+ <item name="android:maxHeight">13dip</item>
+ <item name="android:thumb">@drawable/scrubber_control_selector_holo</item>
+ <item name="android:thumbOffset">16dip</item>
+ <item name="android:focusable">true</item>
+ <item name="android:paddingStart">16dip</item>
+ <item name="android:paddingEnd">16dip</item>
+ <item name="android:mirrorForRtl">true</item>
+ </style>
+
</resources>
mSimState = simState;
updateCarrierText(mSimState, mPlmn, mSpn);
}
+
+ public void onScreenTurnedOff(int why) {
+ setSelected(false);
+ };
+
+ public void onScreenTurnedOn() {
+ setSelected(true);
+ };
};
/**
* The status of this lock screen. Primarily used for widgets on LockScreen.
protected void onFinishInflate() {
super.onFinishInflate();
mSeparator = getResources().getString(R.string.kg_text_message_separator);
- setSelected(true); // Allow marquee to work.
+ final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
+ setSelected(screenOn); // Allow marquee to work.
}
@Override
mAppWidgetContainer.setAddWidgetEnabled(false);
}
checkAppWidgetConsistency();
+
+ // Don't let the user drag the challenge down if widgets are disabled.
+ if (mSlidingChallengeLayout != null) {
+ mSlidingChallengeLayout.setEnableChallengeDragging(!widgetsDisabled());
+ }
+
mSwitchPageRunnable.run();
// This needs to be called after the pages are all added.
mViewStateManager.showUsabilityHints();
mAppWidgetContainer.setVisibility(
isSimOrAccount && fullScreenEnabled ? View.GONE : View.VISIBLE);
+ // Don't show camera or search in navbar when SIM or Account screen is showing
+ setSystemUiVisibility(isSimOrAccount ?
+ (getSystemUiVisibility() | View.STATUS_BAR_DISABLE_SEARCH)
+ : (getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_SEARCH));
+
if (mSlidingChallengeLayout != null) {
mSlidingChallengeLayout.setChallengeInteractive(!fullScreenEnabled);
}
mBatteryIsLow = status.isBatteryLow();
update();
}
+ public void onScreenTurnedOff(int why) {
+ setSelected(false);
+ };
+ public void onScreenTurnedOn() {
+ setSelected(true);
+ };
};
public KeyguardMessageArea(Context context) {
mLockPatternUtils = new LockPatternUtils(context);
- // This is required to ensure marquee works
- setSelected(true);
-
// Registering this callback immediately updates the battery state, among other things.
mUpdateMonitor = KeyguardUpdateMonitor.getInstance(getContext());
mUpdateMonitor.registerCallback(mInfoCallback);
update();
}
+ @Override
+ protected void onFinishInflate() {
+ final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
+ setSelected(screenOn); // This is required to ensure marquee works
+ }
+
public void securityMessageChanged() {
setAlpha(1f);
mShowingMessage = true;
package com.android.keyguard;
import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Typeface;
import android.text.TextUtils;
+import android.text.format.DateFormat;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.Slog;
import android.view.View;
import android.widget.GridLayout;
+import android.widget.TextClock;
import android.widget.TextView;
import com.android.internal.widget.LockPatternUtils;
-import java.text.SimpleDateFormat;
-import java.util.Date;
import java.util.Locale;
-import libcore.icu.ICU;
-
public class KeyguardStatusView extends GridLayout {
private static final boolean DEBUG = KeyguardViewMediator.DEBUG;
private static final String TAG = "KeyguardStatusView";
private LockPatternUtils mLockPatternUtils;
private TextView mAlarmStatusView;
+ private TextClock mDateView;
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
refresh();
}
};
+
+ @Override
+ public void onScreenTurnedOn() {
+ setEnableMarquee(true);
+ };
+
+ @Override
+ public void onScreenTurnedOff(int why) {
+ setEnableMarquee(false);
+ };
};
public KeyguardStatusView(Context context) {
super(context, attrs, defStyle);
}
+ private void setEnableMarquee(boolean enabled) {
+ if (DEBUG) Log.v(TAG, (enabled ? "Enable" : "Disable") + " transport text marquee");
+ if (mAlarmStatusView != null) mAlarmStatusView.setSelected(enabled);
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
-
mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
+ mDateView = (TextClock) findViewById(R.id.date_view);
mLockPatternUtils = new LockPatternUtils(getContext());
-
- // Required to get Marquee to work.
- final View marqueeViews[] = { mAlarmStatusView };
- for (int i = 0; i < marqueeViews.length; i++) {
- View v = marqueeViews[i];
- if (v == null) {
- throw new RuntimeException("Can't find widget at index " + i);
- }
- v.setSelected(true);
- }
+ final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
+ setEnableMarquee(screenOn);
refresh();
}
protected void refresh() {
- refreshAlarmStatus(); // might as well
+ final String fmt = DateFormat.getBestDateTimePattern(Locale.getDefault(),
+ mContext.getResources().getString(R.string.abbrev_wday_month_day_no_year));
+ mDateView.setFormat24Hour(fmt);
+ mDateView.setFormat12Hour(fmt);
+ refreshAlarmStatus();
}
void refreshAlarmStatus() {
import android.graphics.Bitmap;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.MediaMetadataEditor;
KeyguardHostView.TransportControlCallback mTransportControlCallback;
+ private final KeyguardUpdateMonitorCallback mUpdateMonitor
+ = new KeyguardUpdateMonitorCallback() {
+ public void onScreenTurnedOff(int why) {
+ setEnableMarquee(false);
+ };
+ public void onScreenTurnedOn() {
+ setEnableMarquee(true);
+ };
+ };
+
public KeyguardTransportControlView(Context context, AttributeSet attrs) {
super(context, attrs);
if (DEBUG) Log.v(TAG, "Create TCV " + this);
mTransportControlCallback = transportControlCallback;
}
+ private void setEnableMarquee(boolean enabled) {
+ if (DEBUG) Log.v(TAG, (enabled ? "Enable" : "Disable") + " transport text marquee");
+ if (mTrackTitle != null) mTrackTitle.setSelected(enabled);
+ if (mTrackArtistAlbum != null) mTrackTitle.setSelected(enabled);
+ }
+
@Override
public void onFinishInflate() {
super.onFinishInflate();
mMetadataContainer = (ViewGroup) findViewById(R.id.metadata_container);
mBadge = (ImageView) findViewById(R.id.badge);
mTrackTitle = (TextView) findViewById(R.id.title);
- mTrackTitle.setSelected(true); // enable marquee
mTrackArtistAlbum = (TextView) findViewById(R.id.artist_album);
- mTrackArtistAlbum.setSelected(true);
mTransientSeek = findViewById(R.id.transient_seek);
mTransientSeekBar = (SeekBar) findViewById(R.id.transient_seek_bar);
mTransientSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangeListener);
view.setOnClickListener(mTransportCommandListener);
view.setOnLongClickListener(mTransportShowSeekBarListener);
}
+ final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
+ setEnableMarquee(screenOn);
}
@Override
}
if (DEBUG) Log.v(TAG, "Registering TCV " + this);
mAudioManager.registerRemoteController(mRemoteController);
+ KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitor);
}
@Override
super.onDetachedFromWindow();
if (DEBUG) Log.v(TAG, "Unregistering TCV " + this);
mAudioManager.unregisterRemoteController(mRemoteController);
+ KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitor);
mUserSeeking = false;
}
final ColorMatrix cm = new ColorMatrix();
cm.setSaturation(0);
mBadge.setColorFilter(new ColorMatrixColorFilter(cm));
+ mBadge.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
mBadge.setImageAlpha(0xef);
}
protected static final int MSG_SET_PLAYBACK_STATE = 316;
protected static final int MSG_USER_INFO_CHANGED = 317;
protected static final int MSG_REPORT_EMERGENCY_CALL_ACTION = 318;
-
+ private static final int MSG_SCREEN_TURNED_ON = 319;
+ private static final int MSG_SCREEN_TURNED_OFF = 320;
private static KeyguardUpdateMonitor sInstance;
private boolean mSwitchingUser;
+ private boolean mScreenOn;
+
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
case MSG_REPORT_EMERGENCY_CALL_ACTION:
handleReportEmergencyCallAction();
break;
+ case MSG_SCREEN_TURNED_OFF:
+ handleScreenTurnedOff(msg.arg1);
+ break;
+ case MSG_SCREEN_TURNED_ON:
+ handleScreenTurnedOn();
+ break;
}
}
};
return sInstance;
}
+ protected void handleScreenTurnedOn() {
+ final int count = mCallbacks.size();
+ for (int i = 0; i < count; i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onScreenTurnedOn();
+ }
+ }
+ }
+
+ protected void handleScreenTurnedOff(int arg1) {
+ final int count = mCallbacks.size();
+ for (int i = 0; i < count; i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onScreenTurnedOff(arg1);
+ }
+ }
+ }
+
/**
* IMPORTANT: Must be called from UI thread.
*/
public DisplayClientState getCachedDisplayClientState() {
return mDisplayClientState;
}
+
+ // TODO: use these callbacks elsewhere in place of the existing notifyScreen*()
+ // (KeyguardViewMediator, KeyguardHostView)
+ public void dispatchScreenTurnedOn() {
+ synchronized (this) {
+ mScreenOn = true;
+ }
+ mHandler.sendEmptyMessage(MSG_SCREEN_TURNED_ON);
+ }
+
+ public void dispatchScreenTurndOff(int why) {
+ synchronized(this) {
+ mScreenOn = false;
+ }
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_SCREEN_TURNED_OFF, why, 0));
+ }
+
+ public boolean isScreenOn() {
+ return mScreenOn;
+ }
}
import android.app.admin.DevicePolicyManager;
import android.graphics.Bitmap;
import android.media.AudioManager;
+import android.view.WindowManagerPolicy;
import com.android.internal.telephony.IccCardConstants;
*/
void onEmergencyCallAction() { }
+ /**
+ * Called when the transport background changes.
+ * @param bitmap
+ */
public void onSetBackground(Bitmap bitmap) {
- // THIS SPACE FOR RENT
}
+
+ /**
+ * Called when the screen turns on
+ */
+ public void onScreenTurnedOn() { }
+
+ /**
+ * Called when the screen turns off
+ * @param why {@link WindowManagerPolicy#OFF_BECAUSE_OF_USER},
+ * {@link WindowManagerPolicy#OFF_BECAUSE_OF_TIMEOUT} or
+ * {@link WindowManagerPolicy#OFF_BECAUSE_OF_PROX_SENSOR}.
+ */
+ public void onScreenTurnedOff(int why) { }
}
public synchronized void onScreenTurnedOn(final IKeyguardShowCallback callback) {
if (DEBUG) Log.d(TAG, "onScreenTurnedOn()");
mScreenOn = true;
+ final IBinder token = mKeyguardHost == null ? null : mKeyguardHost.getWindowToken();
if (mKeyguardView != null) {
mKeyguardView.onScreenTurnedOn();
mKeyguardHost.post(new Runnable() {
@Override
public void run() {
- IBinder token = null;
- if (mKeyguardHost.getVisibility() == View.VISIBLE) {
- token = mKeyguardHost.getWindowToken();
- }
try {
callback.onShown(token);
} catch (RemoteException e) {
});
} else {
try {
- callback.onShown(null);
+ callback.onShown(token);
} catch (RemoteException e) {
Slog.w(TAG, "Exception calling onShown():", e);
}
}
} else if (callback != null) {
try {
- callback.onShown(null);
+ callback.onShown(token);
} catch (RemoteException e) {
Slog.w(TAG, "Exception calling onShown():", e);
}
doKeyguardLocked(null);
}
}
+ KeyguardUpdateMonitor.getInstance(mContext).dispatchScreenTurndOff(why);
}
private void doKeyguardLaterLocked() {
notifyScreenOnLocked(callback);
}
}
+ KeyguardUpdateMonitor.getInstance(mContext).dispatchScreenTurnedOn();
maybeSendUserPresentBroadcast();
}
private static final boolean DEBUG = false;
// The drag handle is measured in dp above & below the top edge of the
- // challenge view; these parameters change based on whether the challenge
+ // challenge view; these parameters change based on whether the challenge
// is open or closed.
private static final int DRAG_HANDLE_CLOSED_ABOVE = 8; // dp
private static final int DRAG_HANDLE_CLOSED_BELOW = 0; // dp
private int mScrollState;
private OnChallengeScrolledListener mScrollListener;
private OnBouncerStateChangedListener mBouncerListener;
+ private boolean mEnableChallengeDragging;
public static final int SCROLL_STATE_IDLE = 0;
public static final int SCROLL_STATE_DRAGGING = 1;
setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_STABLE | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
}
+ public void setEnableChallengeDragging(boolean enabled) {
+ mEnableChallengeDragging = enabled;
+ }
+
public void setInsets(Rect insets) {
mInsets.set(insets);
}
final float y = ev.getY(i);
if (!mIsBouncing && mActivePointerId == INVALID_POINTER
&& (crossedDragHandle(x, y, mGestureStartY)
- || (isInChallengeView(x, y) &&
+ && shouldEnableChallengeDragging()
+ || (isInChallengeView(x, y) &&
mScrollState == SCROLL_STATE_SETTLING))) {
mActivePointerId = ev.getPointerId(i);
mGestureStartX = x;
mGestureStartChallengeBottom = getChallengeBottom();
mDragging = true;
enableHardwareLayerForChallengeView();
- } else if (mChallengeShowing && isInChallengeView(x, y)) {
+ } else if (mChallengeShowing && isInChallengeView(x, y)
+ && shouldEnableChallengeDragging()) {
mBlockDrag = true;
}
}
return mDragging;
}
+ private boolean shouldEnableChallengeDragging() {
+ return mEnableChallengeDragging || !mChallengeShowing;
+ }
+
private boolean isChallengeInteractionBlocked() {
return !mChallengeInteractiveExternal || !mChallengeInteractiveInternal;
}
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:duplicateParentState="true">
<TextView
android:id="@+id/title"
<item name="android:windowIsTranslucent">true</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:colorBackgroundCacheHint">@android:color/transparent</item>
+ <item name="android:windowIsFloating">true</item>
</style>
<style name="SelectPrinterActivityTheme" parent="@android:style/Theme.Holo.Light">
// TODO: We need some UI for announcing an error.
mControllerState = CONTROLLER_STATE_FAILED;
Log.e(LOG_TAG, "Received invalid pages from the app");
+ mEditor.cancel();
PrintJobConfigActivity.this.finish();
}
}
if (!mFavoritePrinterSelected && mDestinationSpinnerAdapter.getCount() > 2) {
mFavoritePrinterSelected = true;
mDestinationSpinner.setSelection(0);
+ // Workaround again the weird spinner behavior to notify for selection
+ // change on the next layout pass as the current printer is used below.
+ mCurrentPrinter = (PrinterInfo) mDestinationSpinnerAdapter.getItem(0);
}
// If there is a next printer to select and we succeed selecting
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.Filter;
searchView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View view) {
- view.announceForAccessibility(getString(
- R.string.print_search_box_shown_utterance));
+ if (AccessibilityManager.getInstance(getActivity()).isEnabled()) {
+ view.announceForAccessibility(getString(
+ R.string.print_search_box_shown_utterance));
+ }
}
@Override
public void onViewDetachedFromWindow(View view) {
- view.announceForAccessibility(getString(
- R.string.print_search_box_hidden_utterance));
+ Activity activity = getActivity();
+ if (activity != null && !activity.isFinishing()
+ && AccessibilityManager.getInstance(activity).isEnabled()) {
+ view.announceForAccessibility(getString(
+ R.string.print_search_box_hidden_utterance));
+ }
}
});
R.layout.printer_dropdown_item, parent, false);
}
+ convertView.setEnabled(isEnabled(position));
+
CharSequence title = null;
CharSequence subtitle = null;
Drawable icon = null;
}
@Override
+ public boolean isEnabled(int position) {
+ PrinterInfo printer = (PrinterInfo) getItem(position);
+ return printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
+ }
+
+ @Override
public Loader<List<PrinterInfo>> onCreateLoader(int id, Bundle args) {
if (id == LOADER_ID_PRINTERS_LOADER) {
return new FusedPrintersProvider(getActivity());
<!-- Default for Settings.Secure.USER_SETUP_COMPLETE -->
<bool name="def_user_setup_complete">false</bool>
+
+ <!-- Default for Settings.Global.LOW_BATTERY_SOUND_TIMEOUT.
+ 0 means no timeout; battery sounds will always play
+ >0 is milliseconds of screen-off time after which battery sounds will not play -->
+ <integer name="def_low_battery_sound_timeout">0</integer>
</resources>
// database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
// is properly propagated through your change. Not doing so will result in a loss of user
// settings.
- private static final int DATABASE_VERSION = 97;
+ private static final int DATABASE_VERSION = 98;
private Context mContext;
private int mUserHandle;
upgradeVersion = 97;
}
+ if (upgradeVersion == 97) {
+ if (mUserHandle == UserHandle.USER_OWNER) {
+ db.beginTransaction();
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement("INSERT OR REPLACE INTO global(name,value)"
+ + " VALUES(?,?);");
+ loadIntegerSetting(stmt, Settings.Global.LOW_BATTERY_SOUND_TIMEOUT,
+ R.integer.def_low_battery_sound_timeout);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ if (stmt != null) stmt.close();
+ }
+ }
+ upgradeVersion = 98;
+ }
+
// *** Remember to update DATABASE_VERSION above!
if (upgradeVersion != currentVersion) {
CdmaSubscriptionSourceManager.PREFERRED_CDMA_SUBSCRIPTION);
loadSetting(stmt, Settings.Global.CDMA_SUBSCRIPTION_MODE, type);
+ loadIntegerSetting(stmt, Settings.Global.LOW_BATTERY_SOUND_TIMEOUT,
+ R.integer.def_low_battery_sound_timeout);
+
// --- New global settings start here
} finally {
if (stmt != null) stmt.close();
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2011, 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.
-*/
--->
-
-<level-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:maxLevel="4" android:drawable="@drawable/ic_qs_battery_0" />
- <item android:maxLevel="15" android:drawable="@drawable/ic_qs_battery_15" />
- <item android:maxLevel="35" android:drawable="@drawable/ic_qs_battery_28" />
- <item android:maxLevel="49" android:drawable="@drawable/ic_qs_battery_43" />
- <item android:maxLevel="60" android:drawable="@drawable/ic_qs_battery_57" />
- <item android:maxLevel="75" android:drawable="@drawable/ic_qs_battery_71" />
- <item android:maxLevel="90" android:drawable="@drawable/ic_qs_battery_85" />
- <item android:maxLevel="100" android:drawable="@drawable/ic_qs_battery_100" />
-</level-list>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2011, 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.
-*/
--->
-
-<level-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:maxLevel="4" android:drawable="@drawable/ic_qs_battery_charge_0" />
- <item android:maxLevel="15" android:drawable="@drawable/ic_qs_battery_charge_15" />
- <item android:maxLevel="35" android:drawable="@drawable/ic_qs_battery_charge_28" />
- <item android:maxLevel="49" android:drawable="@drawable/ic_qs_battery_charge_43" />
- <item android:maxLevel="60" android:drawable="@drawable/ic_qs_battery_charge_57" />
- <item android:maxLevel="75" android:drawable="@drawable/ic_qs_battery_charge_71" />
- <item android:maxLevel="90" android:drawable="@drawable/ic_qs_battery_charge_85" />
- <item android:maxLevel="100" android:drawable="@drawable/ic_qs_battery_charge_100" />
-</level-list>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2011, 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.
-*/
--->
-
-<level-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:maxLevel="4" android:drawable="@drawable/stat_sys_battery_0" />
- <item android:maxLevel="15" android:drawable="@drawable/stat_sys_battery_15" />
- <item android:maxLevel="35" android:drawable="@drawable/stat_sys_battery_28" />
- <item android:maxLevel="49" android:drawable="@drawable/stat_sys_battery_43" />
- <item android:maxLevel="60" android:drawable="@drawable/stat_sys_battery_57" />
- <item android:maxLevel="75" android:drawable="@drawable/stat_sys_battery_71" />
- <item android:maxLevel="90" android:drawable="@drawable/stat_sys_battery_85" />
- <item android:maxLevel="100" android:drawable="@drawable/stat_sys_battery_100" />
-</level-list>
-
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2011, 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.
-*/
--->
-
-<level-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:maxLevel="4" android:drawable="@drawable/stat_sys_battery_charge_anim0" />
- <item android:maxLevel="15" android:drawable="@drawable/stat_sys_battery_charge_anim15" />
- <item android:maxLevel="35" android:drawable="@drawable/stat_sys_battery_charge_anim28" />
- <item android:maxLevel="49" android:drawable="@drawable/stat_sys_battery_charge_anim43" />
- <item android:maxLevel="60" android:drawable="@drawable/stat_sys_battery_charge_anim57" />
- <item android:maxLevel="75" android:drawable="@drawable/stat_sys_battery_charge_anim71" />
- <item android:maxLevel="90" android:drawable="@drawable/stat_sys_battery_charge_anim85" />
- <item android:maxLevel="100" android:drawable="@drawable/stat_sys_battery_charge_anim100" />
-</level-list>
-
-
<resources>
<drawable name="notification_number_text_color">#ffffffff</drawable>
<drawable name="ticker_background_color">#ff1d1d1d</drawable>
- <drawable name="system_bar_background">#ff000000</drawable>
+ <drawable name="system_bar_background">@color/system_bar_background_opaque</drawable>
+ <color name="system_bar_background_opaque">#ff000000</color>
<color name="system_bar_background_semi_transparent">#66000000</color> <!-- 40% black -->
<color name="notification_panel_solid_background">#ff000000</color>
<drawable name="status_bar_recents_app_thumbnail_background">#88000000</drawable>
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Handler;
+import android.os.PowerManager;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
-import android.util.Log;
+import android.util.Slog;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
AlertDialog mLowBatteryDialog;
TextView mBatteryLevelTextView;
+ private long mScreenOffTime = -1;
+
public void start() {
mLowBatteryAlertCloseLevel = mContext.getResources().getInteger(
mLowBatteryReminderLevels[1] = mContext.getResources().getInteger(
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
+ final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mScreenOffTime = pm.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
+
// Register for Intent broadcasts for...
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(Intent.ACTION_POWER_CONNECTED);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(Intent.ACTION_SCREEN_ON);
mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
}
int bucket = findBatteryLevelBucket(mBatteryLevel);
if (DEBUG) {
- Log.d(TAG, "buckets ....." + mLowBatteryAlertCloseLevel
+ Slog.d(TAG, "buckets ....." + mLowBatteryAlertCloseLevel
+ " .. " + mLowBatteryReminderLevels[0]
+ " .. " + mLowBatteryReminderLevels[1]);
- Log.d(TAG, "level " + oldBatteryLevel + " --> " + mBatteryLevel);
- Log.d(TAG, "status " + oldBatteryStatus + " --> " + mBatteryStatus);
- Log.d(TAG, "plugType " + oldPlugType + " --> " + mPlugType);
- Log.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
- Log.d(TAG, "bucket " + oldBucket + " --> " + bucket);
- Log.d(TAG, "plugged " + oldPlugged + " --> " + plugged);
+ Slog.d(TAG, "level " + oldBatteryLevel + " --> " + mBatteryLevel);
+ Slog.d(TAG, "status " + oldBatteryStatus + " --> " + mBatteryStatus);
+ Slog.d(TAG, "plugType " + oldPlugType + " --> " + mPlugType);
+ Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
+ Slog.d(TAG, "bucket " + oldBucket + " --> " + bucket);
+ Slog.d(TAG, "plugged " + oldPlugged + " --> " + plugged);
}
if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
- Log.d(TAG, "showing invalid charger warning");
+ Slog.d(TAG, "showing invalid charger warning");
showInvalidChargerDialog();
return;
} else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
} else if (mBatteryLevelTextView != null) {
showLowBatteryWarning();
}
+ } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+ mScreenOffTime = SystemClock.elapsedRealtime();
+ } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
+ mScreenOffTime = -1;
} else {
- Log.w(TAG, "unknown intent: " + intent);
+ Slog.w(TAG, "unknown intent: " + intent);
}
}
};
void dismissLowBatteryWarning() {
if (mLowBatteryDialog != null) {
- Log.i(TAG, "closing low battery warning: level=" + mBatteryLevel);
+ Slog.i(TAG, "closing low battery warning: level=" + mBatteryLevel);
mLowBatteryDialog.dismiss();
}
}
void showLowBatteryWarning() {
- Log.i(TAG,
+ Slog.i(TAG,
((mBatteryLevelTextView == null) ? "showing" : "updating")
+ " low battery warning: level=" + mBatteryLevel
+ " [" + findBatteryLevelBucket(mBatteryLevel) + "]");
}
void playLowBatterySound() {
+ final ContentResolver cr = mContext.getContentResolver();
+
+ final int silenceAfter = Settings.Global.getInt(cr,
+ Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0);
+ final long offTime = SystemClock.elapsedRealtime() - mScreenOffTime;
+ if (silenceAfter > 0
+ && mScreenOffTime > 0
+ && offTime > silenceAfter) {
+ Slog.i(TAG, "screen off too long (" + offTime + "ms, limit " + silenceAfter
+ + "ms): not waking up the user with low battery sound");
+ return;
+ }
+
if (DEBUG) {
- Log.i(TAG, "playing low battery sound. WOMP-WOMP!");
+ Slog.d(TAG, "playing low battery sound. pick-a-doop!"); // WOMP-WOMP is deprecated
}
- final ContentResolver cr = mContext.getContentResolver();
if (Settings.Global.getInt(cr, Settings.Global.POWER_SOUNDS_ENABLED, 1) == 1) {
final String soundPath = Settings.Global.getString(cr,
Settings.Global.LOW_BATTERY_SOUND);
}
void showInvalidChargerDialog() {
- Log.d(TAG, "showing invalid charger dialog");
+ Slog.d(TAG, "showing invalid charger dialog");
dismissLowBatteryWarning();
pw.println(Integer.toString(mPlugType));
pw.print("mInvalidCharger=");
pw.println(Integer.toString(mInvalidCharger));
+ pw.print("mScreenOffTime=");
+ pw.print(mScreenOffTime);
+ if (mScreenOffTime >= 0) {
+ pw.print(" (");
+ pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
+ pw.print(" ago)");
+ }
+ pw.println();
+ pw.print("soundTimeout=");
+ pw.println(Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
pw.print("bucket: ");
pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
}
*/
public class SystemBars extends SystemUI implements ServiceMonitor.Callbacks {
private static final String TAG = "SystemBars";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private static final int WAIT_FOR_BARS_TO_DIE = 500;
// manages the implementation coming from the remote process
mOpaque = 0xff0000ff;
mSemiTransparent = 0x7f0000ff;
} else {
- mOpaque = res.getColor(R.drawable.system_bar_background);
+ mOpaque = res.getColor(R.color.system_bar_background_opaque);
mSemiTransparent = res.getColor(R.color.system_bar_background_semi_transparent);
}
public static final boolean DUMPTRUCK = true; // extra dumpsys info
public static final boolean DEBUG_GESTURES = false;
- public static final boolean DEBUG_WINDOW_STATE = true;
+ public static final boolean DEBUG_WINDOW_STATE = false;
public static final boolean SETTINGS_DRAG_SHORTCUT = true;
private AsyncTask<Void, Void, Pair<String, Drawable>> mUserInfoTask;
private AsyncTask<Void, Void, Pair<Boolean, Boolean>> mQueryCertTask;
- private LevelListDrawable mBatteryLevels;
- private LevelListDrawable mChargingBatteryLevels;
-
boolean mTilesSetUp = false;
boolean mUseDefaultAvatar = false;
mHandler = new Handler();
- Resources r = mContext.getResources();
- mBatteryLevels = (LevelListDrawable) r.getDrawable(R.drawable.qs_sys_battery);
- mChargingBatteryLevels =
- (LevelListDrawable) r.getDrawable(R.drawable.qs_sys_battery_charging);
-
IntentFilter filter = new IntentFilter();
filter.addAction(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.systemui.R;
import java.util.ArrayList;
public class BatteryController extends BroadcastReceiver {
private static final String TAG = "StatusBar.BatteryController";
- private Context mContext;
- private ArrayList<ImageView> mIconViews = new ArrayList<ImageView>();
- private ArrayList<TextView> mLabelViews = new ArrayList<TextView>();
private ArrayList<BatteryStateChangeCallback> mChangeCallbacks =
new ArrayList<BatteryStateChangeCallback>();
}
public BatteryController(Context context) {
- mContext = context;
-
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
context.registerReceiver(this, filter);
}
- public void addIconView(ImageView v) {
- mIconViews.add(v);
- }
-
- public void addLabelView(TextView v) {
- mLabelViews.add(v);
- }
-
public void addStateChangedCallback(BatteryStateChangeCallback cb) {
mChangeCallbacks.add(cb);
}
break;
}
- final int icon = plugged ? R.drawable.stat_sys_battery_charge
- : R.drawable.stat_sys_battery;
-
- int N = mIconViews.size();
- for (int i=0; i<N; i++) {
- ImageView v = mIconViews.get(i);
- v.setImageResource(icon);
- v.setImageLevel(level);
- v.setContentDescription(mContext.getString(R.string.accessibility_battery_level,
- level));
- }
- N = mLabelViews.size();
- for (int i=0; i<N; i++) {
- TextView v = mLabelViews.get(i);
- v.setText(mContext.getString(R.string.status_bar_settings_battery_meter_format,
- level));
- }
-
for (BatteryStateChangeCallback cb : mChangeCallbacks) {
cb.onBatteryLevelChanged(level, plugged);
}
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/wallpaper_cropper"
+ android:id="@+id/wallpaper_root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.android.wallpapercropper.CropView
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
+<!-- Copyright (C) 2013 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.
See the License for the specific language governing permissions and
limitations under the License.
-->
-<clip
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/stat_sys_battery_100"
- android:clipOrientation="vertical"
- android:gravity="bottom" />
+<resources>
+ <bool name="allow_rotation">true</bool>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+ <bool name="allow_rotation">false</bool>
+</resources>
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
-import android.view.ViewConfiguration;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
+import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
public interface TouchCallback {
void onTouchDown();
void onTap();
+ void onTouchUp();
}
public CropView(Context context) {
public void onScaleEnd(ScaleGestureDetector detector) {
}
- public void moveToUpperLeft() {
+ public void moveToLeft() {
if (getWidth() == 0 || getHeight() == 0) {
final ViewTreeObserver observer = getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
- moveToUpperLeft();
+ moveToLeft();
getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
getEdgesHelper(edges);
final float scale = mRenderer.scale;
mRenderer.centerX += Math.ceil(edges.left / scale);
- mRenderer.centerY += Math.ceil(edges.top / scale);
}
public void setTouchEnabled(boolean enabled) {
float squaredDist = (mFirstX - x) * (mFirstX - x) + (mFirstY - y) * (mFirstY - y);
float slop = config.getScaledTouchSlop() * config.getScaledTouchSlop();
long now = System.currentTimeMillis();
- // only do this if it's a small movement
- if (mTouchCallback != null &&
- squaredDist < slop &&
+ if (mTouchCallback != null) {
+ // only do this if it's a small movement
+ if (squaredDist < slop &&
now < mTouchDownTime + ViewConfiguration.getTapTimeout()) {
- mTouchCallback.onTap();
+ mTouchCallback.onTap();
+ }
+ mTouchCallback.onTouchUp();
}
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
init();
+ if (!enableRotation()) {
+ setRequestedOrientation(Configuration.ORIENTATION_PORTRAIT);
+ }
}
protected void init() {
cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
}
});
+ getWindow().addPrivateFlags(
+ WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR);
+ }
+
+ public boolean enableRotation() {
+ return getResources().getBoolean(R.bool.allow_rotation);
}
public static String getSharedPreferencesKey() {
}
protected void setWallpaper(String filePath, final boolean finishActivityWhenDone) {
-
BitmapCropTask cropTask = new BitmapCropTask(this,
filePath, null, 0, 0, true, false, null);
final Point bounds = cropTask.getImageBounds();
}
}
};
- BitmapCropTask cropTask = new BitmapCropTask(res, resId,
+ BitmapCropTask cropTask = new BitmapCropTask(this, res, resId,
crop, outSize.x, outSize.y,
true, false, onEndCrop);
cropTask.execute();
protected void cropImageAndSetWallpaper(Uri uri,
OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) {
- // Get the crop
+ // Get the crop
Point inSize = mCropView.getSourceDimensions();
+ boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
+
Point minDims = new Point();
Point maxDims = new Point();
Display d = getWindowManager().getDefaultDisplay();
int maxDim = Math.max(maxDims.x, maxDims.y);
final int minDim = Math.min(minDims.x, minDims.y);
- int defaultWidth;
+ int defaultWallpaperWidth;
if (isScreenLarge(getResources())) {
- defaultWidth = (int) (maxDim *
+ defaultWallpaperWidth = (int) (maxDim *
wallpaperTravelToScreenWidthRatio(maxDim, minDim));
} else {
- defaultWidth = Math.max((int)
+ defaultWallpaperWidth = Math.max((int)
(minDim * WALLPAPER_SCREENS_SPAN), maxDim);
}
// ADJUST CROP WIDTH
// Extend the crop all the way to the right, for parallax
- float extraSpaceToRight = inSize.x - cropRect.right;
+ // (or all the way to the left, in RTL)
+ float extraSpace = ltr ? inSize.x - cropRect.right : cropRect.left;
// Cap the amount of extra width
- float maxExtraSpace = defaultWidth / cropScale - cropRect.width();
- extraSpaceToRight = Math.min(extraSpaceToRight, maxExtraSpace);
+ float maxExtraSpace = defaultWallpaperWidth / cropScale - cropRect.width();
+ extraSpace = Math.min(extraSpace, maxExtraSpace);
- cropRect.right += extraSpaceToRight;
+ if (ltr) {
+ cropRect.right += extraSpace;
+ } else {
+ cropRect.left -= extraSpace;
+ }
// ADJUST CROP HEIGHT
if (isPortrait) {
}
}
};
- BitmapCropTask cropTask = new BitmapCropTask(uri,
+ BitmapCropTask cropTask = new BitmapCropTask(this, uri,
cropRect, outWidth, outHeight, true, false, onEndCrop);
if (onBitmapCroppedHandler != null) {
cropTask.setOnBitmapCropped(onBitmapCroppedHandler);
public void onBitmapCropped(byte[] imageBytes);
}
- protected class BitmapCropTask extends AsyncTask<Void, Void, Boolean> {
+ protected static class BitmapCropTask extends AsyncTask<Void, Void, Boolean> {
Uri mInUri = null;
Context mContext;
String mInFilePath;
RectF mCropBounds = null;
int mOutWidth, mOutHeight;
int mRotation = 0; // for now
- protected final WallpaperManager mWPManager;
String mOutputFormat = "jpg"; // for now
boolean mSetWallpaper;
boolean mSaveCroppedBitmap;
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
mContext = c;
mInFilePath = filePath;
- mWPManager = WallpaperManager.getInstance(getApplicationContext());
init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
}
RectF cropBounds, int outWidth, int outHeight,
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
mInImageBytes = imageBytes;
- mWPManager = WallpaperManager.getInstance(getApplicationContext());
init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
}
- public BitmapCropTask(Uri inUri,
+ public BitmapCropTask(Context c, Uri inUri,
RectF cropBounds, int outWidth, int outHeight,
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
+ mContext = c;
mInUri = inUri;
- mWPManager = WallpaperManager.getInstance(getApplicationContext());
init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
}
- public BitmapCropTask(Resources res, int inResId,
+ public BitmapCropTask(Context c, Resources res, int inResId,
RectF cropBounds, int outWidth, int outHeight,
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
+ mContext = c;
mInResId = inResId;
mResources = res;
- mWPManager = WallpaperManager.getInstance(getApplicationContext());
init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
}
try {
if (mInUri != null) {
mInStream = new BufferedInputStream(
- getContentResolver().openInputStream(mInUri));
+ mContext.getContentResolver().openInputStream(mInUri));
} else if (mInFilePath != null) {
mInStream = mContext.openFileInput(mInFilePath);
} else if (mInImageBytes != null) {
regenerateInputStream();
- if (mNoCrop && mInStream != null) {
+ WallpaperManager wallpaperManager = null;
+ if (mSetWallpaper) {
+ wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext());
+ }
+ if (mSetWallpaper && mNoCrop && mInStream != null) {
try {
- mWPManager.setStream(mInStream);
+ wallpaperManager.setStream(mInStream);
} catch (IOException e) {
Log.w(LOGTAG, "cannot write stream to wallpaper", e);
failure = true;
}
- if (mOnEndRunnable != null) {
- mOnEndRunnable.run();
- }
return !failure;
}
if (mInStream != null) {
(int) returnRect.height(), Bitmap.Config.ARGB_8888);
if (tmp != null) {
Canvas c = new Canvas(tmp);
- c.drawBitmap(crop, m, new Paint());
+ Paint p = new Paint();
+ p.setFilterBitmap(true);
+ c.drawBitmap(crop, m, p);
crop = tmp;
}
} else if (mRotation > 0) {
ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
// If we need to set to the wallpaper, set it
- if (mSetWallpaper && mWPManager != null) {
- if (mWPManager == null) {
- Log.w(LOGTAG, "no wallpaper manager");
- failure = true;
- } else {
- try {
- byte[] outByteArray = tmpOut.toByteArray();
- mWPManager.setStream(new ByteArrayInputStream(outByteArray));
- if (mOnBitmapCroppedHandler != null) {
- mOnBitmapCroppedHandler.onBitmapCropped(outByteArray);
- }
- } catch (IOException e) {
- Log.w(LOGTAG, "cannot write stream to wallpaper", e);
- failure = true;
+ if (mSetWallpaper && wallpaperManager != null) {
+ try {
+ byte[] outByteArray = tmpOut.toByteArray();
+ wallpaperManager.setStream(new ByteArrayInputStream(outByteArray));
+ if (mOnBitmapCroppedHandler != null) {
+ mOnBitmapCroppedHandler.onBitmapCropped(outByteArray);
}
+ } catch (IOException e) {
+ Log.w(LOGTAG, "cannot write stream to wallpaper", e);
+ failure = true;
}
}
- if (mOnEndRunnable != null) {
- mOnEndRunnable.run();
- }
} else {
Log.w(LOGTAG, "cannot compress bitmap");
failure = true;
@Override
protected void onPostExecute(Boolean result) {
- setResult(Activity.RESULT_OK);
- finish();
+ if (mOnEndRunnable != null) {
+ mOnEndRunnable.run();
+ }
}
}
originalView.getWindowToken());
if (helper != null) {
helper.setPresenterCallback(mContextMenuCallback);
+ } else if (mContextMenuHelper != null) {
+ // No menu to show, but if we have a menu currently showing it just became blank.
+ // Close it.
+ mContextMenuHelper.dismiss();
}
mContextMenuHelper = helper;
return helper != null;
*/
static final int SYSTEM_UI_CHANGING_LAYOUT =
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_FULLSCREEN;
+ | View.SYSTEM_UI_FLAG_FULLSCREEN
+ | View.STATUS_BAR_TRANSLUCENT
+ | View.NAVIGATION_BAR_TRANSLUCENT;
/**
* Keyguard stuff
boolean mOrientationSensorEnabled = false;
int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
boolean mHasSoftInput = false;
+ boolean mTouchExplorationEnabled = false;
int mPointerLocationMode = 0; // guarded by mLock
mHasNavigationBar = true;
}
- if (mHasNavigationBar) {
- // The navigation bar is at the right in landscape; it seems always
- // useful to hide it for showing a video.
- mCanHideNavigationBar = true;
- } else {
- mCanHideNavigationBar = false;
- }
-
// For demo purposes, allow the rotation of the HDMI display to be controlled.
// By default, HDMI locks rotation to landscape.
if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
!"true".equals(SystemProperties.get("config.override_forced_orient"));
}
+ /**
+ * @return whether the navigation bar can be hidden, e.g. the device has a
+ * navigation bar and touch exploration is not enabled
+ */
+ private boolean canHideNavigationBar() {
+ return mHasNavigationBar && !mTouchExplorationEnabled;
+ }
+
@Override
public boolean isDefaultOrientationForced() {
return mForceDefaultOrientation;
if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR))
== (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
int availRight, availBottom;
- if (mCanHideNavigationBar &&
+ if (canHideNavigationBar() &&
(systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
availRight = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
availBottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
boolean navTranslucent = (sysui & View.NAVIGATION_BAR_TRANSLUCENT) != 0;
boolean transientAllowed = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
navTranslucent &= !transientAllowed; // transient trumps translucent
+ navTranslucent &= isTranslucentNavigationAllowed();
// When the navigation bar isn't visible, we put up a fake
// input window to catch all touch events. This way we can
// For purposes of positioning and showing the nav bar, if we have
// decided that it can't be hidden (because of the screen aspect ratio),
// then take that into account.
- navVisible |= !mCanHideNavigationBar;
+ navVisible |= !canHideNavigationBar();
boolean updateSysUiVisibility = false;
if (mNavigationBar != null) {
dcf.bottom = mSystemBottom;
final boolean inheritTranslucentDecor = (attrs.privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0;
- if (attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
- && attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW
- && !inheritTranslucentDecor) {
+ final boolean isAppWindow =
+ attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW &&
+ attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+ final boolean topAtRest =
+ win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw();
+ if (isAppWindow && !inheritTranslucentDecor && !topAtRest) {
if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0
&& (fl & WindowManager.LayoutParams.FLAG_FULLSCREEN) == 0
&& (fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) == 0) {
pf.right = df.right = of.right = mOverscanScreenLeft + mOverscanScreenWidth;
pf.bottom = df.bottom = of.bottom = mOverscanScreenTop
+ mOverscanScreenHeight;
- } else if (mCanHideNavigationBar
+ } else if (canHideNavigationBar()
&& (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
&& attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
&& attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
= mOverscanScreenLeft + mOverscanScreenWidth;
pf.bottom = df.bottom = of.bottom = cf.bottom
= mOverscanScreenTop + mOverscanScreenHeight;
- } else if (mCanHideNavigationBar
+ } else if (canHideNavigationBar()
&& (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
&& (attrs.type == TYPE_TOAST
|| (attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
})) {
return;
}
+ Slog.i(TAG, "No lock screen! waitForWindowDrawn false");
+
} catch (RemoteException ex) {
// Can't happen in system process.
}
}
- Slog.i(TAG, "No lock screen!");
+ Slog.i(TAG, "No lock screen! windowToken=" + windowToken);
finishScreenTurningOn(screenOnListener);
}
vis = (vis & ~flags) | (oldVis & flags);
}
+ if (!isTranslucentNavigationAllowed()) {
+ vis &= ~View.NAVIGATION_BAR_TRANSLUCENT;
+ }
+
// update status bar
boolean transientAllowed =
(vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
&& (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
}
+ /**
+ * @return whether the navigation bar can be made translucent, e.g. touch
+ * exploration is not enabled
+ */
+ private boolean isTranslucentNavigationAllowed() {
+ return !mTouchExplorationEnabled;
+ }
+
// Use this instead of checking config_showNavigationBar so that it can be consistently
// overridden by qemu.hw.mainkeys in the emulator.
@Override
}
@Override
+ public void setTouchExplorationEnabled(boolean enabled) {
+ mTouchExplorationEnabled = enabled;
+ }
+
+ @Override
public boolean isTopLevelWindow(int windowType) {
if (windowType >= WindowManager.LayoutParams.FIRST_SUB_WINDOW
&& windowType <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
addrTried ++) {
// Choose the address at random but make sure its type is supported
+ // TODO: This doesn't work 100% of the time, because we may end up
+ // trying the same invalid address more than once and ignoring one
+ // of the valid addresses.
InetAddress hostAddr = addresses[rand.nextInt(addresses.length)];
if (((hostAddr instanceof Inet4Address) && linkHasIpv4)
|| ((hostAddr instanceof Inet6Address) && linkHasIpv6)) {
}
// Rewrite the url to have numeric address to use the specific route.
- // I also set the "Connection" to "Close" as by default "Keep-Alive"
- // is used which is useless in this case.
- URL newUrl = new URL(orgUri.getScheme() + "://"
- + hostAddr.getHostAddress() + orgUri.getPath());
+ URL newUrl = new URL(orgUri.getScheme(),
+ hostAddr.getHostAddress(), orgUri.getPath());
log("isMobileOk: newUrl=" + newUrl);
HttpURLConnection urlConn = null;
urlConn.setReadTimeout(SOCKET_TIMEOUT_MS);
urlConn.setUseCaches(false);
urlConn.setAllowUserInteraction(false);
+ // Set the "Connection" to "Close" as by default "Keep-Alive"
+ // is used which is useless in this case.
urlConn.setRequestProperty("Connection", "close");
int responseCode = urlConn.getResponseCode();
descriptionId, primary, removable, emulated, mtpReserve,
allowMassStorage, maxFileSize, null);
addVolumeLocked(volume);
+
+ // Until we hear otherwise, treat as unmounted
+ mVolumeStates.put(volume.getPath(), Environment.MEDIA_UNMOUNTED);
}
}
final StorageVolume v = mVolumes.get(i);
pw.print(" ");
pw.println(v.toString());
+ pw.println(" state=" + mVolumeStates.get(v.getPath()));
}
}
Settings.Secure.TOUCH_EXPLORATION_ENABLED, enabled ? 1 : 0,
userState.mUserId);
}
+ try {
+ mWindowManagerService.setTouchExplorationEnabled(enabled);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
}
private boolean canRequestAndRequestsTouchExplorationLocked(Service service) {
import java.util.List;
import android.os.Handler;
+import android.os.Looper;
import android.util.ArrayMap;
import com.android.internal.app.ProcessStats;
import com.android.internal.os.BatteryStatsImpl;
static final int MSG_BG_START_TIMEOUT = 1;
- ServiceMap(int userId) {
+ ServiceMap(Looper looper, int userId) {
+ super(looper);
mUserId = userId;
}
private ServiceMap getServiceMap(int callingUser) {
ServiceMap smap = mServiceMap.get(callingUser);
if (smap == null) {
- smap = new ServiceMap(callingUser);
+ smap = new ServiceMap(mAm.mHandler.getLooper(), callingUser);
mServiceMap.put(callingUser, smap);
}
return smap;
int[] users = mAm.getUsersLocked();
if ("all".equals(name)) {
for (int user : users) {
- ArrayMap<ComponentName, ServiceRecord> alls = getServices(user);
+ ServiceMap smap = mServiceMap.get(user);
+ if (smap == null) {
+ continue;
+ }
+ ArrayMap<ComponentName, ServiceRecord> alls = smap.mServicesByName;
for (int i=0; i<alls.size(); i++) {
ServiceRecord r1 = alls.valueAt(i);
services.add(r1);
}
for (int user : users) {
- ArrayMap<ComponentName, ServiceRecord> alls = getServices(user);
+ ServiceMap smap = mServiceMap.get(user);
+ if (smap == null) {
+ continue;
+ }
+ ArrayMap<ComponentName, ServiceRecord> alls = smap.mServicesByName;
for (int i=0; i<alls.size(); i++) {
ServiceRecord r1 = alls.valueAt(i);
if (componentName != null) {
static final boolean DEBUG_RESULTS = localLOGV || false;
static final boolean DEBUG_SERVICE = localLOGV || false;
static final boolean DEBUG_SERVICE_EXECUTING = localLOGV || false;
- static final boolean DEBUG_STACK = localLOGV || false;
+ static final boolean DEBUG_STACK = localLOGV || true;
static final boolean DEBUG_SWITCH = localLOGV || false;
static final boolean DEBUG_TASKS = localLOGV || false;
static final boolean DEBUG_THUMBNAILS = localLOGV || false;
private void cleanUpRemovedTaskLocked(TaskRecord tr, int flags) {
tr.disposeThumbnail();
mRecentTasks.remove(tr);
- mStackSupervisor.removeTask(tr);
final boolean killProcesses = (flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0;
Intent baseIntent = new Intent(
tr.intent != null ? tr.intent : tr.affinityIntent);
+ cpr.appInfo.packageName + ": " + e);
}
- ProcessRecord proc = startProcessLocked(cpi.processName,
- cpr.appInfo, false, 0, "content provider",
- new ComponentName(cpi.applicationInfo.packageName,
- cpi.name), false, false, false);
- if (proc == null) {
- Slog.w(TAG, "Unable to launch app "
- + cpi.applicationInfo.packageName + "/"
- + cpi.applicationInfo.uid + " for provider "
- + name + ": process is bad");
- return null;
+ // Use existing process if already started
+ ProcessRecord proc = getProcessRecordLocked(
+ cpi.processName, cpr.appInfo.uid, false);
+ if (proc != null) {
+ if (DEBUG_PROVIDER) {
+ Slog.d(TAG, "Installing in existing process " + proc);
+ }
+ proc.pubProviders.put(cpi.name, cpr);
+ try {
+ proc.thread.scheduleInstallProvider(cpi);
+ } catch (RemoteException e) {
+ }
+ } else {
+ proc = startProcessLocked(cpi.processName,
+ cpr.appInfo, false, 0, "content provider",
+ new ComponentName(cpi.applicationInfo.packageName,
+ cpi.name), false, false, false);
+ if (proc == null) {
+ Slog.w(TAG, "Unable to launch app "
+ + cpi.applicationInfo.packageName + "/"
+ + cpi.applicationInfo.uid + " for provider "
+ + name + ": process is bad");
+ return null;
+ }
}
cpr.launchingApp = proc;
mLaunchingProviders.add(cpr);
return;
}
+ long ident = Binder.clearCallingIdentity();
+ try {
+ dumpInner(fd, pw, args);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void dumpInner(FileDescriptor fd, PrintWriter pw, String[] args) {
final long now = SystemClock.uptimeMillis();
boolean isCheckin = false;
// All available services, for your resolving pleasure.
final ServiceIntentResolver mServices = new ServiceIntentResolver();
- // Keys are String (provider class name), values are Provider.
- final HashMap<ComponentName, PackageParser.Provider> mProvidersByComponent =
- new HashMap<ComponentName, PackageParser.Provider>();
+ // All available providers, for your resolving pleasure.
+ final ProviderIntentResolver mProviders = new ProviderIntentResolver();
// Mapping from provider base names (first directory in content URI codePath)
// to the provider information.
- final HashMap<String, PackageParser.Provider> mProviders =
+ final HashMap<String, PackageParser.Provider> mProvidersByAuthority =
new HashMap<String, PackageParser.Provider>();
// Mapping from instrumentation class names to info about them.
if (!sUserManager.exists(userId)) return null;
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get provider info");
synchronized (mPackages) {
- PackageParser.Provider p = mProvidersByComponent.get(component);
+ PackageParser.Provider p = mProviders.mProviders.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getProviderInfo " + component + ": " + p);
if (p != null && mSettings.isEnabledLPr(p.info, flags, userId)) {
}
@Override
+ public List<ResolveInfo> queryIntentContentProviders(
+ Intent intent, String resolvedType, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return Collections.emptyList();
+ ComponentName comp = intent.getComponent();
+ if (comp == null) {
+ if (intent.getSelector() != null) {
+ intent = intent.getSelector();
+ comp = intent.getComponent();
+ }
+ }
+ if (comp != null) {
+ final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+ final ProviderInfo pi = getProviderInfo(comp, flags, userId);
+ if (pi != null) {
+ final ResolveInfo ri = new ResolveInfo();
+ ri.providerInfo = pi;
+ list.add(ri);
+ }
+ return list;
+ }
+
+ // reader
+ synchronized (mPackages) {
+ String pkgName = intent.getPackage();
+ if (pkgName == null) {
+ return mProviders.queryIntent(intent, resolvedType, flags, userId);
+ }
+ final PackageParser.Package pkg = mPackages.get(pkgName);
+ if (pkg != null) {
+ return mProviders.queryIntentForPackage(
+ intent, resolvedType, flags, pkg.providers, userId);
+ }
+ return null;
+ }
+ }
+
+ @Override
public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
if (!sUserManager.exists(userId)) return null;
// reader
synchronized (mPackages) {
- final PackageParser.Provider provider = mProviders.get(name);
+ final PackageParser.Provider provider = mProvidersByAuthority.get(name);
PackageSetting ps = provider != null
? mSettings.mPackages.get(provider.owner.packageName)
: null;
public void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo) {
// reader
synchronized (mPackages) {
- final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProviders.entrySet()
- .iterator();
+ final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProvidersByAuthority
+ .entrySet().iterator();
final int userId = UserHandle.getCallingUserId();
while (i.hasNext()) {
Map.Entry<String, PackageParser.Provider> entry = i.next();
ArrayList<ProviderInfo> finalList = null;
// reader
synchronized (mPackages) {
- final Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator();
+ final Iterator<PackageParser.Provider> i = mProviders.mProviders.values().iterator();
final int userId = processName != null ?
UserHandle.getUserId(uid) : UserHandle.getCallingUserId();
while (i.hasNext()) {
if (p.info.authority != null) {
String names[] = p.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
- if (mProviders.containsKey(names[j])) {
- PackageParser.Provider other = mProviders.get(names[j]);
+ if (mProvidersByAuthority.containsKey(names[j])) {
+ PackageParser.Provider other = mProvidersByAuthority.get(names[j]);
Slog.w(TAG, "Can't install because provider name " + names[j] +
" (in package " + pkg.applicationInfo.packageName +
") is already used by "
PackageParser.Provider p = pkg.providers.get(i);
p.info.processName = fixProcessName(pkg.applicationInfo.processName,
p.info.processName, pkg.applicationInfo.uid);
- mProvidersByComponent.put(new ComponentName(p.info.packageName,
- p.info.name), p);
+ mProviders.addProvider(p);
p.syncable = p.info.isSyncable;
if (p.info.authority != null) {
String names[] = p.info.authority.split(";");
p = new PackageParser.Provider(p);
p.syncable = false;
}
- if (!mProviders.containsKey(names[j])) {
- mProviders.put(names[j], p);
+ if (!mProvidersByAuthority.containsKey(names[j])) {
+ mProvidersByAuthority.put(names[j], p);
if (p.info.authority == null) {
p.info.authority = names[j];
} else {
+ p.info.isSyncable);
}
} else {
- PackageParser.Provider other = mProviders.get(names[j]);
+ PackageParser.Provider other = mProvidersByAuthority.get(names[j]);
Slog.w(TAG, "Skipping provider name " + names[j] +
" (in package " + pkg.applicationInfo.packageName +
"): name already used by "
int i;
for (i=0; i<N; i++) {
PackageParser.Provider p = pkg.providers.get(i);
- mProvidersByComponent.remove(new ComponentName(p.info.packageName,
- p.info.name));
+ mProviders.removeProvider(p);
if (p.info.authority == null) {
/* There was another ContentProvider with this authority when
}
String names[] = p.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
- if (mProviders.get(names[j]) == p) {
- mProviders.remove(names[j]);
+ if (mProvidersByAuthority.get(names[j]) == p) {
+ mProvidersByAuthority.remove(names[j]);
if (DEBUG_REMOVE) {
if (chatty)
Log.d(TAG, "Unregistered content provider: " + names[j]
private int mFlags;
};
+ private final class ProviderIntentResolver
+ extends IntentResolver<PackageParser.ProviderIntentInfo, ResolveInfo> {
+ public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
+ boolean defaultOnly, int userId) {
+ mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
+ return super.queryIntent(intent, resolvedType, defaultOnly, userId);
+ }
+
+ public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,
+ int userId) {
+ if (!sUserManager.exists(userId))
+ return null;
+ mFlags = flags;
+ return super.queryIntent(intent, resolvedType,
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);
+ }
+
+ public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
+ int flags, ArrayList<PackageParser.Provider> packageProviders, int userId) {
+ if (!sUserManager.exists(userId))
+ return null;
+ if (packageProviders == null) {
+ return null;
+ }
+ mFlags = flags;
+ final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
+ final int N = packageProviders.size();
+ ArrayList<PackageParser.ProviderIntentInfo[]> listCut =
+ new ArrayList<PackageParser.ProviderIntentInfo[]>(N);
+
+ ArrayList<PackageParser.ProviderIntentInfo> intentFilters;
+ for (int i = 0; i < N; ++i) {
+ intentFilters = packageProviders.get(i).intents;
+ if (intentFilters != null && intentFilters.size() > 0) {
+ PackageParser.ProviderIntentInfo[] array =
+ new PackageParser.ProviderIntentInfo[intentFilters.size()];
+ intentFilters.toArray(array);
+ listCut.add(array);
+ }
+ }
+ return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
+ }
+
+ public final void addProvider(PackageParser.Provider p) {
+ mProviders.put(p.getComponentName(), p);
+ if (DEBUG_SHOW_INFO) {
+ Log.v(TAG, " "
+ + (p.info.nonLocalizedLabel != null
+ ? p.info.nonLocalizedLabel : p.info.name) + ":");
+ Log.v(TAG, " Class=" + p.info.name);
+ }
+ final int NI = p.intents.size();
+ int j;
+ for (j = 0; j < NI; j++) {
+ PackageParser.ProviderIntentInfo intent = p.intents.get(j);
+ if (DEBUG_SHOW_INFO) {
+ Log.v(TAG, " IntentFilter:");
+ intent.dump(new LogPrinter(Log.VERBOSE, TAG), " ");
+ }
+ if (!intent.debugCheck()) {
+ Log.w(TAG, "==> For Provider " + p.info.name);
+ }
+ addFilter(intent);
+ }
+ }
+
+ public final void removeProvider(PackageParser.Provider p) {
+ mProviders.remove(p.getComponentName());
+ if (DEBUG_SHOW_INFO) {
+ Log.v(TAG, " " + (p.info.nonLocalizedLabel != null
+ ? p.info.nonLocalizedLabel : p.info.name) + ":");
+ Log.v(TAG, " Class=" + p.info.name);
+ }
+ final int NI = p.intents.size();
+ int j;
+ for (j = 0; j < NI; j++) {
+ PackageParser.ProviderIntentInfo intent = p.intents.get(j);
+ if (DEBUG_SHOW_INFO) {
+ Log.v(TAG, " IntentFilter:");
+ intent.dump(new LogPrinter(Log.VERBOSE, TAG), " ");
+ }
+ removeFilter(intent);
+ }
+ }
+
+ @Override
+ protected boolean allowFilterResult(
+ PackageParser.ProviderIntentInfo filter, List<ResolveInfo> dest) {
+ ProviderInfo filterPi = filter.provider.info;
+ for (int i = dest.size() - 1; i >= 0; i--) {
+ ProviderInfo destPi = dest.get(i).providerInfo;
+ if (destPi.name == filterPi.name
+ && destPi.packageName == filterPi.packageName) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ protected PackageParser.ProviderIntentInfo[] newArray(int size) {
+ return new PackageParser.ProviderIntentInfo[size];
+ }
+
+ @Override
+ protected boolean isFilterStopped(PackageParser.ProviderIntentInfo filter, int userId) {
+ if (!sUserManager.exists(userId))
+ return true;
+ PackageParser.Package p = filter.provider.owner;
+ if (p != null) {
+ PackageSetting ps = (PackageSetting) p.mExtras;
+ if (ps != null) {
+ // System apps are never considered stopped for purposes of
+ // filtering, because there may be no way for the user to
+ // actually re-launch them.
+ return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0
+ && ps.getStopped(userId);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean isPackageForFilter(String packageName,
+ PackageParser.ProviderIntentInfo info) {
+ return packageName.equals(info.provider.owner.packageName);
+ }
+
+ @Override
+ protected ResolveInfo newResult(PackageParser.ProviderIntentInfo filter,
+ int match, int userId) {
+ if (!sUserManager.exists(userId))
+ return null;
+ final PackageParser.ProviderIntentInfo info = filter;
+ if (!mSettings.isEnabledLPr(info.provider.info, mFlags, userId)) {
+ return null;
+ }
+ final PackageParser.Provider provider = info.provider;
+ if (mSafeMode && (provider.info.applicationInfo.flags
+ & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ return null;
+ }
+ PackageSetting ps = (PackageSetting) provider.owner.mExtras;
+ if (ps == null) {
+ return null;
+ }
+ ProviderInfo pi = PackageParser.generateProviderInfo(provider, mFlags,
+ ps.readUserState(userId), userId);
+ if (pi == null) {
+ return null;
+ }
+ final ResolveInfo res = new ResolveInfo();
+ res.providerInfo = pi;
+ if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
+ res.filter = filter;
+ }
+ res.priority = info.getPriority();
+ res.preferredOrder = provider.owner.mPreferredOrder;
+ res.match = match;
+ res.isDefault = info.hasDefault;
+ res.labelRes = info.labelRes;
+ res.nonLocalizedLabel = info.nonLocalizedLabel;
+ res.icon = info.icon;
+ res.system = isSystemApp(res.providerInfo.applicationInfo);
+ return res;
+ }
+
+ @Override
+ protected void sortResults(List<ResolveInfo> results) {
+ Collections.sort(results, mResolvePrioritySorter);
+ }
+
+ @Override
+ protected void dumpFilter(PrintWriter out, String prefix,
+ PackageParser.ProviderIntentInfo filter) {
+ out.print(prefix);
+ out.print(
+ Integer.toHexString(System.identityHashCode(filter.provider)));
+ out.print(' ');
+ filter.provider.printComponentShortName(out);
+ out.print(" filter ");
+ out.println(Integer.toHexString(System.identityHashCode(filter)));
+ }
+
+ private final HashMap<ComponentName, PackageParser.Provider> mProviders
+ = new HashMap<ComponentName, PackageParser.Provider>();
+ private int mFlags;
+ };
+
private static final Comparator<ResolveInfo> mResolvePrioritySorter =
new Comparator<ResolveInfo>() {
public int compare(ResolveInfo r1, ResolveInfo r2) {
dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) {
dumpState.setTitlePrinted(true);
}
+ if (mProviders.dump(pw, dumpState.getTitlePrinted() ? "\nProvider Resolver Table:"
+ : "Provider Resolver Table:", " ", packageName,
+ dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) {
+ dumpState.setTitlePrinted(true);
+ }
}
if (dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
if (dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
boolean printedSomething = false;
- for (PackageParser.Provider p : mProvidersByComponent.values()) {
+ for (PackageParser.Provider p : mProviders.mProviders.values()) {
if (packageName != null && !packageName.equals(p.info.packageName)) {
continue;
}
pw.print(" "); pw.println(p.toString());
}
printedSomething = false;
- for (Map.Entry<String, PackageParser.Provider> entry : mProviders.entrySet()) {
+ for (Map.Entry<String, PackageParser.Provider> entry :
+ mProvidersByAuthority.entrySet()) {
PackageParser.Provider p = entry.getValue();
if (packageName != null && !packageName.equals(p.info.packageName)) {
continue;
import android.app.ActivityManager.StackBoxInfo;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.Debug;
import android.util.Slog;
import android.view.Display;
import android.view.DisplayInfo;
* @return true if a change was made, false otherwise.
*/
boolean moveHomeStackBox(boolean toTop) {
- if (DEBUG_STACK) Slog.d(TAG, "moveHomeStackBox: toTop=" + toTop);
+ if (DEBUG_STACK) Slog.d(TAG, "moveHomeStackBox: toTop=" + toTop + " Callers=" +
+ Debug.getCallers(4));
switch (mStackBoxes.size()) {
case 0: throw new RuntimeException("moveHomeStackBox: No home StackBox!");
case 1: return false; // Only the home StackBox exists.
}
private void draw(Rect bounds, int color) {
- if (DEBUG_STACK) Slog.i(TAG, "draw: bounds=" + bounds.toShortString() +
+ if (false && DEBUG_STACK) Slog.i(TAG, "draw: bounds=" + bounds.toShortString() +
" color=" + Integer.toHexString(color));
mTmpDrawRect.set(bounds);
Canvas c = null;
}
private void positionSurface(Rect bounds) {
- if (DEBUG_STACK) Slog.i(TAG, "positionSurface: bounds=" + bounds.toShortString());
+ if (false && DEBUG_STACK) Slog.i(TAG, "positionSurface: bounds=" + bounds.toShortString());
mSurfaceControl.setSize(bounds.width(), bounds.height());
mSurfaceControl.setPosition(bounds.left, bounds.top);
}
// Note: caller responsible for being inside
// Surface.openTransaction() / closeTransaction()
public void setVisibility(boolean on) {
- if (DEBUG_STACK) Slog.i(TAG, "setVisibility: on=" + on +
+ if (false && DEBUG_STACK) Slog.i(TAG, "setVisibility: on=" + on +
" mLastBounds=" + mLastBounds.toShortString() +
" mBounds=" + mBounds.toShortString());
if (mSurfaceControl == null) {
}
public void setBounds(Rect bounds) {
- if (DEBUG_STACK) Slog.i(TAG, "setBounds: bounds=" + bounds);
+ if (false && DEBUG_STACK) Slog.i(TAG, "setBounds: bounds=" + bounds);
mBounds.set(bounds);
}
static final boolean DEBUG_SURFACE_TRACE = false;
static final boolean DEBUG_WINDOW_TRACE = false;
static final boolean DEBUG_TASK_MOVEMENT = false;
- static final boolean DEBUG_STACK = false;
+ static final boolean DEBUG_STACK = true;
static final boolean SHOW_SURFACE_ALLOC = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS;
}
if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && win==mCurrentFocus) Slog.v(
- TAG, "Remove " + win + " client="
- + Integer.toHexString(System.identityHashCode(win.mClient.asBinder()))
- + ", surface=" + win.mWinAnimator.mSurfaceControl,
- new RuntimeException("here").fillInStackTrace());
+ TAG, "Remove " + win + " client="
+ + Integer.toHexString(System.identityHashCode(win.mClient.asBinder()))
+ + ", surface=" + win.mWinAnimator.mSurfaceControl + " Callers="
+ + Debug.getCallers(4));
final long origId = Binder.clearCallingIdentity();
mInputManager.setInputFilter(filter);
}
+ @Override
+ public void setTouchExplorationEnabled(boolean enabled) {
+ mPolicy.setTouchExplorationEnabled(enabled);
+ }
+
public void setCurrentUser(final int newUserId) {
synchronized (mWindowMap) {
int oldUserId = mCurrentUserId;
//Slog.i(TAG, "Waiting for drawn " + win + ": removed="
// + win.mRemoved + " visible=" + win.isVisibleLw()
// + " shown=" + win.mSurfaceShown);
- if (win.mRemoved || !win.isVisibleLw()) {
- // Window has been removed or made invisible; no draw
- // will now happen, so stop waiting.
+ if (win.mRemoved) {
+ // Window has been removed; no draw will now happen, so stop waiting.
Slog.w(TAG, "Aborted waiting for drawn: " + pair.first);
try {
pair.second.sendResult(null);
checkDrawnWindowsLocked();
return true;
}
+ Slog.i(TAG, "waitForWindowDrawn: win null");
}
}
return false;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
+import android.location.Country;
import android.location.CountryDetector;
import android.net.Uri;
import android.provider.ContactsContract.CommonDataKinds.Phone;
* is in.
*/
private static String getCurrentCountryIso(Context context, Locale locale) {
- String countryIso;
- CountryDetector detector = (CountryDetector) context.getSystemService(
- Context.COUNTRY_DETECTOR);
- if (detector != null) {
- countryIso = detector.detectCountry().getCountryIso();
- } else {
- countryIso = locale.getCountry();
- Rlog.w(TAG, "No CountryDetector; falling back to countryIso based on locale: "
- + countryIso);
- }
- return countryIso;
+ String countryIso = null;
+ CountryDetector detector = (CountryDetector) context.getSystemService(
+ Context.COUNTRY_DETECTOR);
+ if (detector != null) {
+ Country country = detector.detectCountry();
+ if (country != null) {
+ countryIso = country.getCountryIso();
+ } else {
+ Rlog.e(TAG, "CountryDetector.detectCountry() returned null.");
+ }
+ }
+ if (countryIso == null) {
+ countryIso = locale.getCountry();
+ Rlog.w(TAG, "No CountryDetector; falling back to countryIso based on locale: "
+ + countryIso);
+ }
+ return countryIso;
}
/**
throw new UnsupportedOperationException();
}
+ /** @hide */
+ @Override
+ public List<ResolveInfo> queryIntentContentProvidersAsUser(
+ Intent intent, int flags, int userId) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<ResolveInfo> queryIntentContentProviders(Intent intent, int flags) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public ProviderInfo resolveContentProvider(String name, int flags) {
throw new UnsupportedOperationException();
return false;
}
+/*
+ * A simple container that holds a resource type and name. It is ordered first by type then
+ * by name.
+ */
+struct type_ident_pair_t {
+ String16 type;
+ String16 ident;
+
+ type_ident_pair_t() { };
+ type_ident_pair_t(const String16& t, const String16& i) : type(t), ident(i) { }
+ type_ident_pair_t(const type_ident_pair_t& o) : type(o.type), ident(o.ident) { }
+ inline bool operator < (const type_ident_pair_t& o) const {
+ int cmp = compare_type(type, o.type);
+ if (cmp < 0) {
+ return true;
+ } else if (cmp > 0) {
+ return false;
+ } else {
+ return strictly_order_type(ident, o.ident);
+ }
+ }
+};
+
+
status_t parseAndAddEntry(Bundle* bundle,
const sp<AaptFile>& in,
ResXMLTree* block,
const String16& product,
bool pseudolocalize,
const bool overwrite,
+ KeyedVector<type_ident_pair_t, bool>* skippedResourceNames,
ResourceTable* outTable)
{
status_t err;
if (bundleProduct[0] == '\0') {
if (strcmp16(String16("default").string(), product.string()) != 0) {
+ /*
+ * This string has a product other than 'default'. Do not add it,
+ * but record it so that if we do not see the same string with
+ * product 'default' or no product, then report an error.
+ */
+ skippedResourceNames->replaceValueFor(
+ type_ident_pair_t(curType, ident), true);
return NO_ERROR;
}
} else {
DefaultKeyedVector<String16, uint32_t> nextPublicId(0);
+ // Stores the resource names that were skipped. Typically this happens when
+ // AAPT is invoked without a product specified and a resource has no
+ // 'default' product attribute.
+ KeyedVector<type_ident_pair_t, bool> skippedResourceNames;
+
ResXMLTree::event_code_t code;
do {
code = block.next();
err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident,
*curTag, curIsStyled, curFormat, curIsFormatted,
- product, false, overwrite, outTable);
+ product, false, overwrite, &skippedResourceNames, outTable);
if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR?
hasErrors = localHasErrors = true;
err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType,
ident, *curTag, curIsStyled, curFormat,
curIsFormatted, product,
- true, overwrite, outTable);
+ true, overwrite, &skippedResourceNames, outTable);
if (err != NO_ERROR) {
hasErrors = localHasErrors = true;
}
}
}
+ // For every resource defined, there must be exist one variant with a product attribute
+ // set to 'default' (or no product attribute at all).
+ // We check to see that for every resource that was ignored because of a mismatched
+ // product attribute, some product variant of that resource was processed.
+ for (size_t i = 0; i < skippedResourceNames.size(); i++) {
+ if (skippedResourceNames[i]) {
+ const type_ident_pair_t& p = skippedResourceNames.keyAt(i);
+ if (!outTable->hasBagOrEntry(myPackage, p.type, p.ident)) {
+ const char* bundleProduct =
+ (bundle->getProduct() == NULL) ? "" : bundle->getProduct();
+ fprintf(stderr, "In resource file %s: %s\n",
+ in->getPrintableSource().string(),
+ curParams.toString().string());
+
+ fprintf(stderr, "\t%s '%s' does not match product %s.\n"
+ "\tYou may have forgotten to include a 'default' product variant"
+ " of the resource.\n",
+ String8(p.type).string(), String8(p.ident).string(),
+ bundleProduct[0] == 0 ? "default" : bundleProduct);
+ return UNKNOWN_ERROR;
+ }
+ }
+ }
+
return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
}
String16 comment(c->getComment());
typeSymbols->appendComment(String8(c->getName()), comment, c->getPos());
- //printf("Type symbol %s comment: %s\n", String8(e->getName()).string(),
- // String8(comment).string());
+ //printf("Type symbol [%08x] %s comment: %s\n", rid,
+ // String8(c->getName()).string(), String8(comment).string());
comment = c->getTypeComment();
typeSymbols->appendTypeComment(String8(c->getName()), comment);
} else {
// TODO Auto-generated method stub
return false;
}
+
+ @Override
+ public void setTouchExplorationEnabled(boolean enabled) {
+ }
}