$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/media/video/*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/effects/)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/framework-res_intermediates)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS";
field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
- field public static final java.lang.String BIND_CALL_SERVICE = "android.permission.BIND_CALL_SERVICE";
field public static final java.lang.String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
- field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
- field public static final java.lang.String BIND_PRINT_SPOOLER_SERVICE = "android.permission.BIND_PRINT_SPOOLER_SERVICE";
field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE";
field public static final java.lang.String BIND_VPN_SERVICE = "android.permission.BIND_VPN_SERVICE";
field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String MANAGE_ACCOUNTS = "android.permission.MANAGE_ACCOUNTS";
field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
- field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
field public static final java.lang.String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
method public void clear() throws java.io.IOException;
method public void clearWallpaperOffsets(android.os.IBinder);
method public void forgetLoadedWallpaper();
+ method public android.graphics.drawable.Drawable getBuiltInDrawable();
+ method public android.graphics.drawable.Drawable getBuiltInDrawable(int, int, boolean, float, float);
method public android.content.Intent getCropAndSetWallpaperIntent(android.net.Uri);
method public int getDesiredMinimumHeight();
method public int getDesiredMinimumWidth();
method public static java.util.List<android.content.SyncInfo> getCurrentSyncs();
method public static int getIsSyncable(android.accounts.Account, java.lang.String);
method public static boolean getMasterSyncAutomatically();
+ method public java.util.List<android.content.UriPermission> getOutgoingPersistedUriPermissions();
method public static java.util.List<android.content.PeriodicSync> getPeriodicSyncs(android.accounts.Account, java.lang.String);
method public java.util.List<android.content.UriPermission> getPersistedUriPermissions();
method public java.lang.String[] getStreamTypes(android.net.Uri, 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
public final class AccessibilityManager {
method public boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
+ method public boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
method public deprecated java.util.List<android.content.pm.ServiceInfo> getAccessibilityServiceList();
method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int);
method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList();
method public boolean isEnabled();
method public boolean isTouchExplorationEnabled();
method public boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
+ method public boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
method public void sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
}
method public abstract void onAccessibilityStateChanged(boolean);
}
+ public static abstract interface AccessibilityManager.TouchExplorationStateChangeListener {
+ method public abstract void onTouchExplorationStateChanged(boolean);
+ }
+
public class AccessibilityNodeInfo implements android.os.Parcelable {
method public void addAction(int);
method public void addChild(android.view.View);
private long mChangingStagger = 0;
/**
+ * Static interpolators - these are stateless and can be shared across the instances
+ */
+ private static TimeInterpolator ACCEL_DECEL_INTERPOLATOR =
+ new AccelerateDecelerateInterpolator();
+ private static TimeInterpolator DECEL_INTERPOLATOR = new DecelerateInterpolator();
+ private static TimeInterpolator sAppearingInterpolator = ACCEL_DECEL_INTERPOLATOR;
+ private static TimeInterpolator sDisappearingInterpolator = ACCEL_DECEL_INTERPOLATOR;
+ private static TimeInterpolator sChangingAppearingInterpolator = DECEL_INTERPOLATOR;
+ private static TimeInterpolator sChangingDisappearingInterpolator = DECEL_INTERPOLATOR;
+ private static TimeInterpolator sChangingInterpolator = DECEL_INTERPOLATOR;
+
+ /**
* The default interpolators used for the animations
*/
- private TimeInterpolator mAppearingInterpolator = new AccelerateDecelerateInterpolator();
- private TimeInterpolator mDisappearingInterpolator = new AccelerateDecelerateInterpolator();
- private TimeInterpolator mChangingAppearingInterpolator = new DecelerateInterpolator();
- private TimeInterpolator mChangingDisappearingInterpolator = new DecelerateInterpolator();
- private TimeInterpolator mChangingInterpolator = new DecelerateInterpolator();
+ private TimeInterpolator mAppearingInterpolator = sAppearingInterpolator;
+ private TimeInterpolator mDisappearingInterpolator = sDisappearingInterpolator;
+ private TimeInterpolator mChangingAppearingInterpolator = sChangingAppearingInterpolator;
+ private TimeInterpolator mChangingDisappearingInterpolator = sChangingDisappearingInterpolator;
+ private TimeInterpolator mChangingInterpolator = sChangingInterpolator;
/**
* These hashmaps are used to store the animations that are currently running as part of
case APPEARING:
startDelay = mChangingAppearingDelay + staggerDelay;
staggerDelay += mChangingAppearingStagger;
+ if (mChangingAppearingInterpolator != sChangingAppearingInterpolator) {
+ anim.setInterpolator(mChangingAppearingInterpolator);
+ }
break;
case DISAPPEARING:
startDelay = mChangingDisappearingDelay + staggerDelay;
staggerDelay += mChangingDisappearingStagger;
+ if (mChangingDisappearingInterpolator !=
+ sChangingDisappearingInterpolator) {
+ anim.setInterpolator(mChangingDisappearingInterpolator);
+ }
break;
case CHANGING:
startDelay = mChangingDelay + staggerDelay;
staggerDelay += mChangingStagger;
+ if (mChangingInterpolator != sChangingInterpolator) {
+ anim.setInterpolator(mChangingInterpolator);
+ }
break;
}
anim.setStartDelay(startDelay);
anim.setTarget(child);
anim.setStartDelay(mAppearingDelay);
anim.setDuration(mAppearingDuration);
+ if (mAppearingInterpolator != sAppearingInterpolator) {
+ anim.setInterpolator(mAppearingInterpolator);
+ }
if (anim instanceof ObjectAnimator) {
((ObjectAnimator) anim).setCurrentPlayTime(0);
}
Animator anim = mDisappearingAnim.clone();
anim.setStartDelay(mDisappearingDelay);
anim.setDuration(mDisappearingDuration);
+ if (mDisappearingInterpolator != sDisappearingInterpolator) {
+ anim.setInterpolator(mDisappearingInterpolator);
+ }
anim.setTarget(child);
final float preAnimAlpha = child.getAlpha();
anim.addListener(new AnimatorListenerAdapter() {
}
/**
- * Report to the system that your app is now fully drawn. This is only used
- * to help instrument app launch times, so that the app can report when it is
- * fully in a usable state; without this, all the system can determine is when
- * its window is first drawn and displayed. To participate in app launch time
+ * Report to the system that your app is now fully drawn, purely for diagnostic
+ * purposes (calling it does not impact the visible behavior of the activity).
+ * This is only used to help instrument application launch times, so that the
+ * app can report when it is fully in a usable state; without this, the only thing
+ * the system itself can determine is the point at which the activity's window
+ * is <em>first</em> drawn and displayed. To participate in app launch time
* measurement, you should always call this method after first launch (when
- * {@link #onCreate(android.os.Bundle)} is called) at the point where you have
+ * {@link #onCreate(android.os.Bundle)} is called), at the point where you have
* entirely drawn your UI and populated with all of the significant data. You
* can safely call this method any time after first launch as well, in which case
* it will simply be ignored.
/**
* Permits an application to erase its own data from disk. This is equivalent to
- * the user choosing to clear the app's data from within the device settings UI.
+ * the user choosing to clear the app's data from within the device settings UI. It
+ * erases all dynamic data associated with the app -- its private data and data in its
+ * private area on external storage -- but does not remove the installed application
+ * itself, nor any OBB files.
*
* @return {@code true} if the application successfully requested that the application's
* data be erased; {@code false} otherwise.
* not be done on a UI thread. The data will be written to the given file
* descriptor as text. An application must hold the
* {@link android.Manifest.permission#DUMP} permission to make this call.
- * @param fd The file descriptor that the dump should be written to.
+ * @param fd The file descriptor that the dump should be written to. The file
+ * descriptor is <em>not</em> closed by this function; the caller continues to
+ * own it.
* @param packageName The name of the package that is to be dumped.
*/
public void dumpPackageState(FileDescriptor fd, String packageName) {
case GET_PERSISTED_URI_PERMISSIONS_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
- final ParceledListSlice<UriPermission> perms = getPersistedUriPermissions();
+ final String packageName = data.readString();
+ final boolean incoming = data.readInt() != 0;
+ final ParceledListSlice<UriPermission> perms = getPersistedUriPermissions(
+ packageName, incoming);
reply.writeNoException();
perms.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
return true;
}
@Override
- public ParceledListSlice<UriPermission> getPersistedUriPermissions() throws RemoteException {
+ public ParceledListSlice<UriPermission> getPersistedUriPermissions(
+ String packageName, boolean incoming) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(packageName);
+ data.writeInt(incoming ? 1 : 0);
mRemote.transact(GET_PERSISTED_URI_PERMISSIONS_TRANSACTION, data, reply, 0);
reply.readException();
final ParceledListSlice<UriPermission> perms = ParceledListSlice.CREATOR.createFromParcel(
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>();
/**
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();
+ }
}
int mode) throws RemoteException;
public void takePersistableUriPermission(Uri uri, int modeFlags) throws RemoteException;
public void releasePersistableUriPermission(Uri uri, int modeFlags) throws RemoteException;
- public ParceledListSlice<UriPermission> getPersistedUriPermissions() throws RemoteException;
+ public ParceledListSlice<UriPermission> getPersistedUriPermissions(
+ String packageName, boolean incoming) throws RemoteException;
public void showWaitingForDebugger(IApplicationThread who, boolean waiting)
throws RemoteException;
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;
}
package android.app;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.BitmapRegionDecoder;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Retrieve the current system wallpaper; if
- * no wallpaper is set, the system default wallpaper is returned.
+ * no wallpaper is set, the system built-in static wallpaper is returned.
* This is returned as an
* abstract Drawable that you can install in a View to display whatever
* wallpaper the user has currently set.
}
/**
+ * Returns a drawable for the system built-in static wallpaper .
+ *
+ */
+ public Drawable getBuiltInDrawable() {
+ return getBuiltInDrawable(0, 0, false, 0, 0);
+ }
+
+ /**
+ * Returns a drawable for the system built-in static wallpaper. Based on the parameters, the
+ * drawable can be cropped and scaled
+ *
+ * @param outWidth The width of the returned drawable
+ * @param outWidth The height of the returned drawable
+ * @param scaleToFit If true, scale the wallpaper down rather than just cropping it
+ * @param horizontalAlignment A float value between 0 and 1 specifying where to crop the image;
+ * 0 for left-aligned, 0.5 for horizontal center-aligned, and 1 for right-aligned
+ * @param verticalAlignment A float value between 0 and 1 specifying where to crop the image;
+ * 0 for top-aligned, 0.5 for vertical center-aligned, and 1 for bottom-aligned
+ *
+ */
+ public Drawable getBuiltInDrawable(int outWidth, int outHeight,
+ boolean scaleToFit, float horizontalAlignment, float verticalAlignment) {
+ if (sGlobals.mService == null) {
+ Log.w(TAG, "WallpaperService not running");
+ return null;
+ }
+ Resources resources = mContext.getResources();
+ horizontalAlignment = Math.max(0, Math.min(1, horizontalAlignment));
+ verticalAlignment = Math.max(0, Math.min(1, verticalAlignment));
+
+ InputStream is = new BufferedInputStream(
+ resources.openRawResource(com.android.internal.R.drawable.default_wallpaper));
+
+ if (is == null) {
+ Log.e(TAG, "default wallpaper input stream is null");
+ return null;
+ } else {
+ if (outWidth <= 0 || outHeight <= 0) {
+ Bitmap fullSize = BitmapFactory.decodeStream(is, null, null);
+ return new BitmapDrawable(resources, fullSize);
+ } else {
+ int inWidth;
+ int inHeight;
+ {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(is, null, options);
+ if (options.outWidth != 0 && options.outHeight != 0) {
+ inWidth = options.outWidth;
+ inHeight = options.outHeight;
+ } else {
+ Log.e(TAG, "default wallpaper dimensions are 0");
+ return null;
+ }
+ }
+
+ is = new BufferedInputStream(resources.openRawResource(
+ com.android.internal.R.drawable.default_wallpaper));
+
+ RectF cropRectF;
+
+ outWidth = Math.min(inWidth, outWidth);
+ outHeight = Math.min(inHeight, outHeight);
+ if (scaleToFit) {
+ cropRectF = getMaxCropRect(inWidth, inHeight, outWidth, outHeight,
+ horizontalAlignment, verticalAlignment);
+ } else {
+ float left = (inWidth - outWidth) * horizontalAlignment;
+ float right = left + outWidth;
+ float top = (inHeight - outHeight) * verticalAlignment;
+ float bottom = top + outHeight;
+ cropRectF = new RectF(bottom, left, right, top);
+ }
+ Rect roundedTrueCrop = new Rect();
+ cropRectF.roundOut(roundedTrueCrop);
+
+ if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) {
+ Log.w(TAG, "crop has bad values for full size image");
+ return null;
+ }
+
+ // See how much we're reducing the size of the image
+ int scaleDownSampleSize = Math.min(roundedTrueCrop.width() / outWidth,
+ roundedTrueCrop.height() / outHeight);
+
+ // Attempt to open a region decoder
+ BitmapRegionDecoder decoder = null;
+ try {
+ decoder = BitmapRegionDecoder.newInstance(is, true);
+ } catch (IOException e) {
+ Log.w(TAG, "cannot open region decoder for default wallpaper");
+ }
+
+ Bitmap crop = null;
+ if (decoder != null) {
+ // Do region decoding to get crop bitmap
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ if (scaleDownSampleSize > 1) {
+ options.inSampleSize = scaleDownSampleSize;
+ }
+ crop = decoder.decodeRegion(roundedTrueCrop, options);
+ decoder.recycle();
+ }
+
+ if (crop == null) {
+ // BitmapRegionDecoder has failed, try to crop in-memory
+ is = new BufferedInputStream(resources.openRawResource(
+ com.android.internal.R.drawable.default_wallpaper));
+ Bitmap fullSize = null;
+ if (is != null) {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ if (scaleDownSampleSize > 1) {
+ options.inSampleSize = scaleDownSampleSize;
+ }
+ fullSize = BitmapFactory.decodeStream(is, null, options);
+ }
+ if (fullSize != null) {
+ crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left,
+ roundedTrueCrop.top, roundedTrueCrop.width(),
+ roundedTrueCrop.height());
+ }
+ }
+
+ if (crop == null) {
+ Log.w(TAG, "cannot decode default wallpaper");
+ return null;
+ }
+
+ // Scale down if necessary
+ if (outWidth > 0 && outHeight > 0 &&
+ (crop.getWidth() != outWidth || crop.getHeight() != outHeight)) {
+ Matrix m = new Matrix();
+ RectF cropRect = new RectF(0, 0, crop.getWidth(), crop.getHeight());
+ RectF returnRect = new RectF(0, 0, outWidth, outHeight);
+ m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
+ Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(),
+ (int) returnRect.height(), Bitmap.Config.ARGB_8888);
+ if (tmp != null) {
+ Canvas c = new Canvas(tmp);
+ Paint p = new Paint();
+ p.setFilterBitmap(true);
+ c.drawBitmap(crop, m, p);
+ crop = tmp;
+ }
+ }
+
+ return new BitmapDrawable(resources, crop);
+ }
+ }
+ }
+
+ private static RectF getMaxCropRect(int inWidth, int inHeight, int outWidth, int outHeight,
+ float horizontalAlignment, float verticalAlignment) {
+ RectF cropRect = new RectF();
+ // Get a crop rect that will fit this
+ if (inWidth / (float) inHeight > outWidth / (float) outHeight) {
+ cropRect.top = 0;
+ cropRect.bottom = inHeight;
+ float cropWidth = outWidth * (inHeight / (float) outHeight);
+ cropRect.left = (inWidth - cropWidth) * horizontalAlignment;
+ cropRect.right = cropRect.left + cropWidth;
+ } else {
+ cropRect.left = 0;
+ cropRect.right = inWidth;
+ float cropHeight = outHeight * (inWidth / (float) outWidth);
+ cropRect.top = (inHeight - cropHeight) * verticalAlignment;
+ cropRect.bottom = cropRect.top + cropHeight;
+ }
+ return cropRect;
+ }
+
+ /**
* Retrieve the current system wallpaper; if there is no wallpaper set,
* a null pointer is returned. This is returned as an
* abstract Drawable that you can install in a View to display whatever
*
* @param resid The bitmap to save.
*
- * @throws IOException If an error occurs reverting to the default
+ * @throws IOException If an error occurs reverting to the built-in
* wallpaper.
*/
public void setResource(int resid) throws IOException {
*
* @param bitmap The bitmap to save.
*
- * @throws IOException If an error occurs reverting to the default
+ * @throws IOException If an error occurs reverting to the built-in
* wallpaper.
*/
public void setBitmap(Bitmap bitmap) throws IOException {
*
* @param data A stream containing the raw data to install as a wallpaper.
*
- * @throws IOException If an error occurs reverting to the default
+ * @throws IOException If an error occurs reverting to the built-in
* wallpaper.
*/
public void setStream(InputStream data) throws IOException {
}
/**
- * Remove any currently set wallpaper, reverting to the system's default
+ * Remove any currently set wallpaper, reverting to the system's built-in
* wallpaper. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
* is broadcast.
*
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#SET_WALLPAPER}.
*
- * @throws IOException If an error occurs reverting to the default
+ * @throws IOException If an error occurs reverting to the built-in
* wallpaper.
*/
public void clear() throws IOException {
}
}
- private ServiceConnection mConnection = new ServiceConnection() {
+ private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
mService = IBluetoothA2dp.Stub.asInterface(service);
/**
* Broadcast Action: This intent is used to broadcast PAIRING REQUEST
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} to
+ * receive.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PAIRING_REQUEST =
return false;
}
- private ServiceConnection mConnection = new ServiceConnection() {
+ private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
mService = IBluetoothHeadset.Stub.asInterface(service);
mServiceListener = null;
}
- private ServiceConnection mConnection = new ServiceConnection() {
+ private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
mService = IBluetoothHealth.Stub.asInterface(service);
return BluetoothProfile.PRIORITY_OFF;
}
- private ServiceConnection mConnection = new ServiceConnection() {
+ private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
mService = IBluetoothInputDevice.Stub.asInterface(service);
try {
mService = null;
mContext.unbindService(mConnection);
- mConnection = null;
} catch (Exception re) {
Log.e(TAG,"",re);
}
return PRIORITY_OFF;
}
- private ServiceConnection mConnection = new ServiceConnection() {
+ private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) log("Proxy object connected");
mService = IBluetoothMap.Stub.asInterface(service);
/*package*/ void close() {
if (VDBG) log("close()");
- if (mConnection != null) {
- mContext.unbindService(mConnection);
- mConnection = null;
+
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.unregisterStateChangeCallback(mStateChangeCallback);
+ } catch (RemoteException re) {
+ Log.w(TAG,"Unable to unregister BluetoothStateChangeCallback",re);
+ }
}
- mServiceListener = null;
- try {
- mAdapter.getBluetoothManager().unregisterStateChangeCallback(mStateChangeCallback);
- } catch (RemoteException re) {
- Log.w(TAG,"Unable to register BluetoothStateChangeCallback",re);
+
+ synchronized (mConnection) {
+ if (mPanService != null) {
+ try {
+ mPanService = null;
+ mContext.unbindService(mConnection);
+ } catch (Exception re) {
+ Log.e(TAG,"",re);
+ }
+ }
}
+ mServiceListener = null;
}
protected void finalize() {
close();
}
- private IBluetoothStateChangeCallback mStateChangeCallback = new IBluetoothStateChangeCallback.Stub() {
+ final private IBluetoothStateChangeCallback mStateChangeCallback = new IBluetoothStateChangeCallback.Stub() {
@Override
public void onBluetoothStateChange(boolean on) throws RemoteException {
return false;
}
- private ServiceConnection mConnection = new ServiceConnection() {
+ private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected");
mPanService = IBluetoothPan.Stub.asInterface(service);
try {
mService = null;
mContext.unbindService(mConnection);
- mConnection = null;
} catch (Exception re) {
Log.e(TAG,"",re);
}
}
}
- private ServiceConnection mConnection = new ServiceConnection() {
+ private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) log("Proxy object connected");
mService = IBluetoothPbap.Stub.asInterface(service);
}
/**
- * Return list of all Uri permission grants that have been persisted for the
- * calling app. Only persistable grants taken with
+ * Return list of all Uri permission grants that have been persisted by the
+ * calling app. That is, the returned permissions have been granted
+ * <em>to</em> the calling app. Only persistable grants taken with
* {@link #takePersistableUriPermission(Uri, int)} are returned.
*
* @see #takePersistableUriPermission(Uri, int)
*/
public List<UriPermission> getPersistedUriPermissions() {
try {
- return ActivityManagerNative.getDefault().getPersistedUriPermissions().getList();
+ return ActivityManagerNative.getDefault()
+ .getPersistedUriPermissions(mPackageName, true).getList();
+ } catch (RemoteException e) {
+ throw new RuntimeException("Activity manager has died", e);
+ }
+ }
+
+ /**
+ * Return list of all persisted Uri permission grants that are hosted by the
+ * calling app. That is, the returned permissions have been granted
+ * <em>from</em> the calling app. Only grants taken with
+ * {@link #takePersistableUriPermission(Uri, int)} are returned.
+ */
+ public List<UriPermission> getOutgoingPersistedUriPermissions() {
+ try {
+ return ActivityManagerNative.getDefault()
+ .getPersistedUriPermissions(mPackageName, false).getList();
} catch (RemoteException e) {
throw new RuntimeException("Activity manager has died", e);
}
* included in the filter, then an Intent's data must match one of
* them. If no scheme specific parts are included, then only the scheme must match.
*
+ * <p>The "scheme specific part" that this matches against is the string returned
+ * by {@link android.net.Uri#getSchemeSpecificPart() Uri.getSchemeSpecificPart}.
+ * For Uris that contain a path, this kind of matching is not generally of interest,
+ * since {@link #addDataAuthority(String, String)} and
+ * {@link #addDataPath(String, int)} can provide a better mechanism for matching
+ * them. However, for Uris that do not contain a path, the authority and path
+ * are empty, so this is the only way to match against the non-scheme part.</p>
+ *
* @param ssp Either a raw string that must exactly match the scheme specific part
* path, or a simple pattern, depending on <var>type</var>.
* @param type Determines how <var>ssp</var> will be compared to
private CaptureRequest(CaptureRequest source) {
mSettings = new CameraMetadataNative(source.mSettings);
mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
+ mUserTag = source.mUserTag;
}
/**
mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService);
mRootView = mInflater.inflate(
com.android.internal.R.layout.input_method, null);
+ mRootView.setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
mWindow.setContentView(mRootView);
mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
if (Settings.Global.getInt(getContentResolver(),
// If PAC URL is present in either then they must be equal.
// Other parameters will only be for fall back.
if (!TextUtils.isEmpty(mPacFileUrl)) {
- return mPacFileUrl.equals(p.getPacFileUrl());
+ return mPacFileUrl.equals(p.getPacFileUrl()) && mPort == p.mPort;
}
if (!TextUtils.isEmpty(p.getPacFileUrl())) {
return false;
if (mPacFileUrl != null) {
dest.writeByte((byte)1);
dest.writeString(mPacFileUrl);
+ dest.writeInt(mPort);
return;
} else {
dest.writeByte((byte)0);
String host = null;
int port = 0;
if (in.readByte() != 0) {
- return new ProxyProperties(in.readString());
+ String url = in.readString();
+ int localPort = in.readInt();
+ return new ProxyProperties(url, localPort);
}
if (in.readByte() != 0) {
host = in.readString();
int sequence);
void writePrintJobData(in ParcelFileDescriptor fd, in PrintJobId printJobId);
void setClient(IPrintSpoolerClient client);
+ void setPrintJobCancelling(in PrintJobId printJobId, boolean cancelling);
}
/** Information about the printed document. */
private PrintDocumentInfo mDocumentInfo;
+ /** Whether we are trying to cancel this print job. */
+ private boolean mCanceling;
+
/** @hide*/
public PrintJobInfo() {
/* do nothing */
mPageRanges = other.mPageRanges;
mAttributes = other.mAttributes;
mDocumentInfo = other.mDocumentInfo;
+ mCanceling = other.mCanceling;
}
private PrintJobInfo(Parcel parcel) {
if (parcel.readInt() == 1) {
mDocumentInfo = PrintDocumentInfo.CREATOR.createFromParcel(parcel);
}
+ mCanceling = (parcel.readInt() == 1);
}
/**
mDocumentInfo = info;
}
+ /**
+ * Gets whether this print is being cancelled.
+ *
+ * @return True if the print job is being cancelled.
+ *
+ * @hide
+ */
+ public boolean isCancelling() {
+ return mCanceling;
+ }
+
+ /**
+ * Sets whether this print is being cancelled.
+ *
+ * @param cancelling True if the print job is being cancelled.
+ *
+ * @hide
+ */
+ public void setCancelling(boolean cancelling) {
+ mCanceling = cancelling;
+ }
+
@Override
public int describeContents() {
return 0;
} else {
parcel.writeInt(0);
}
+ parcel.writeInt(mCanceling ? 1 : 0);
}
@Override
? mAttributes.toString() : null));
builder.append(", documentInfo: " + (mDocumentInfo != null
? mDocumentInfo.toString() : null));
+ builder.append(", cancelling: " + mCanceling);
builder.append(", pages: " + (mPageRanges != null
? Arrays.toString(mPageRanges) : null));
builder.append("}");
* <p>
* To obtain a handle to the print manager do the following:
* </p>
+ *
* <pre>
* PrintManager printManager =
* (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
private static final boolean DEBUG = false;
+ private static final int MSG_START_PRINT_JOB_CONFIG_ACTIVITY = 1;
+ private static final int MSG_NOTIFY_PRINT_JOB_STATE_CHANGED = 2;
+
/** @hide */
public static final int APP_ID_ANY = -2;
/**
* Callback notifying that a print job state changed.
- *
+ *
* @param printJobId The print job id.
*/
- public void onPrintJobsStateChanged(PrintJobId printJobId);
+ public void onPrintJobStateChanged(PrintJobId printJobId);
}
/**
* Creates a new instance.
- *
+ *
* @param context The current context in which to operate.
* @param service The backing system service.
- *
* @hide
*/
public PrintManager(Context context, IPrintManager service, int userId, int appId) {
mHandler = new Handler(context.getMainLooper(), null, false) {
@Override
public void handleMessage(Message message) {
- SomeArgs args = (SomeArgs) message.obj;
- Context context = (Context) args.arg1;
- IntentSender intent = (IntentSender) args.arg2;
- args.recycle();
- try {
- context.startIntentSender(intent, null, 0, 0, 0);
- } catch (SendIntentException sie) {
- Log.e(LOG_TAG, "Couldn't start print job config activity.", sie);
+ switch (message.what) {
+ case MSG_START_PRINT_JOB_CONFIG_ACTIVITY: {
+ SomeArgs args = (SomeArgs) message.obj;
+ Context context = (Context) args.arg1;
+ IntentSender intent = (IntentSender) args.arg2;
+ args.recycle();
+ try {
+ context.startIntentSender(intent, null, 0, 0, 0);
+ } catch (SendIntentException sie) {
+ Log.e(LOG_TAG, "Couldn't start print job config activity.", sie);
+ }
+ }
+ break;
+
+ case MSG_NOTIFY_PRINT_JOB_STATE_CHANGED: {
+ SomeArgs args = (SomeArgs) message.obj;
+ PrintJobStateChangeListener listener =
+ (PrintJobStateChangeListener) args.arg1;
+ PrintJobId printJobId = (PrintJobId) args.arg2;
+ args.recycle();
+ listener.onPrintJobStateChanged(printJobId);
+ }
+ break;
}
}
};
/**
* Creates an instance that can access all print jobs.
- *
+ *
* @param userId The user id for which to get all print jobs.
- * @return An instance if the caller has the permission to access
- * all print jobs, null otherwise.
+ * @return An instance if the caller has the permission to access all print
+ * jobs, null otherwise.
* @hide
*/
public PrintManager getGlobalPrintManagerForUser(int userId) {
/**
* Adds a listener for observing the state of print jobs.
- *
+ *
* @param listener The listener to add.
- *
* @hide
*/
public void addPrintJobStateChangeListener(PrintJobStateChangeListener listener) {
PrintJobStateChangeListenerWrapper>();
}
PrintJobStateChangeListenerWrapper wrappedListener =
- new PrintJobStateChangeListenerWrapper(listener);
+ new PrintJobStateChangeListenerWrapper(listener, mHandler);
try {
mService.addPrintJobStateChangeListener(wrappedListener, mAppId, mUserId);
mPrintJobStateChangeListeners.put(listener, wrappedListener);
/**
* Removes a listener for observing the state of print jobs.
- *
+ *
* @param listener The listener to remove.
- *
* @hide
*/
public void removePrintJobStateChangeListener(PrintJobStateChangeListener listener) {
/**
* Gets a print job given its id.
- *
+ *
* @return The print job list.
- *
* @see PrintJob
- *
* @hide
*/
public PrintJob getPrintJob(PrintJobId printJobId) {
/**
* Gets the print jobs for this application.
- *
+ *
* @return The print job list.
- *
* @see PrintJob
*/
public List<PrintJob> getPrintJobs() {
}
/**
- * Creates a print job for printing a {@link PrintDocumentAdapter} with default print
- * attributes.
- *
+ * Creates a print job for printing a {@link PrintDocumentAdapter} with
+ * default print attributes.
+ *
* @param printJobName A name for the new print job.
* @param documentAdapter An adapter that emits the document to print.
* @param attributes The default print job attributes.
/**
* Gets the list of enabled print services.
- *
+ *
* @return The enabled service list or an empty list.
- *
* @hide
*/
public List<PrintServiceInfo> getEnabledPrintServices() {
/**
* Gets the list of installed print services.
- *
+ *
* @return The installed service list or an empty list.
- *
* @hide
*/
public List<PrintServiceInfo> getInstalledPrintServices() {
SomeArgs args = SomeArgs.obtain();
args.arg1 = manager.mContext;
args.arg2 = intent;
- manager.mHandler.obtainMessage(0, args).sendToTarget();
+ manager.mHandler.obtainMessage(MSG_START_PRINT_JOB_CONFIG_ACTIVITY,
+ args).sendToTarget();
}
}
}
private CancellationSignal mLayoutOrWriteCancellation;
- private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in finish()
+ private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK -
+ // cleared in finish()
private Handler mHandler; // Strong reference OK - cleared in finish()
switch (message.what) {
case MSG_START: {
mDocumentAdapter.onStart();
- } break;
+ }
+ break;
case MSG_LAYOUT: {
final CancellationSignal cancellation;
new MyLayoutResultCallback(layoutSpec.callback,
layoutSpec.sequence), layoutSpec.metadata);
}
- } break;
+ }
+ break;
case MSG_WRITE: {
final CancellationSignal cancellation;
final WriteSpec writeSpec;
synchronized (mLock) {
- writeSpec= mLastWriteSpec;
+ writeSpec = mLastWriteSpec;
mLastWriteSpec = null;
cancellation = new CancellationSignal();
mLayoutOrWriteCancellation = cancellation;
cancellation, new MyWriteResultCallback(writeSpec.callback,
writeSpec.fd, writeSpec.sequence));
}
- } break;
+ }
+ break;
case MSG_FINISH: {
if (DEBUG) {
}
mDocumentAdapter.onFinish();
doFinish();
- } break;
+ }
+ break;
default: {
throw new IllegalArgumentException("Unknown message: "
private static final class PrintJobStateChangeListenerWrapper extends
IPrintJobStateChangeListener.Stub {
private final WeakReference<PrintJobStateChangeListener> mWeakListener;
+ private final WeakReference<Handler> mWeakHandler;
- public PrintJobStateChangeListenerWrapper(PrintJobStateChangeListener listener) {
+ public PrintJobStateChangeListenerWrapper(PrintJobStateChangeListener listener,
+ Handler handler) {
mWeakListener = new WeakReference<PrintJobStateChangeListener>(listener);
+ mWeakHandler = new WeakReference<Handler>(handler);
}
@Override
public void onPrintJobStateChanged(PrintJobId printJobId) {
+ Handler handler = mWeakHandler.get();
PrintJobStateChangeListener listener = mWeakListener.get();
- if (listener != null) {
- listener.onPrintJobsStateChanged(printJobId);
+ if (handler != null && listener != null) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = listener;
+ args.arg2 = printJobId;
+ handler.obtainMessage(MSG_NOTIFY_PRINT_JOB_STATE_CHANGED,
+ args).sendToTarget();
}
}
}
+
}
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.graphics.Bitmap;
private DocumentsContract() {
}
- /** {@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 EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME";
+
+ /** {@hide} */
public static final String ACTION_MANAGE_ROOT = "android.provider.action.MANAGE_ROOT";
/** {@hide} */
public static final String ACTION_MANAGE_DOCUMENT = "android.provider.action.MANAGE_DOCUMENT";
return false;
}
- final ProviderInfo info = context.getPackageManager()
- .resolveContentProvider(uri.getAuthority(), PackageManager.GET_META_DATA);
- if (info != null && info.metaData != null && info.metaData.containsKey(
- DocumentsContract.META_DATA_DOCUMENT_PROVIDER)) {
- return true;
+ final Intent intent = new Intent(PROVIDER_INTERFACE);
+ final List<ResolveInfo> infos = context.getPackageManager()
+ .queryIntentContentProviders(intent, 0);
+ for (ResolveInfo info : infos) {
+ if (uri.getAuthority().equals(info.providerInfo.authority)) {
+ return true;
+ }
}
return false;
}
/**
* 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.
*/
mPrivateFlags |= PFLAG_DRAWN;
- if (((mViewFlags & VISIBILITY_MASK) == INVISIBLE) && hasFocus()) {
+ if (((mViewFlags & VISIBILITY_MASK) == INVISIBLE)) {
// root view becoming invisible shouldn't clear focus and accessibility focus
if (getRootView() != this) {
- clearFocus();
+ if (hasFocus()) clearFocus();
clearAccessibilityFocus();
}
}
* @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;
boolean mIsTouchExplorationEnabled;
- final CopyOnWriteArrayList<AccessibilityStateChangeListener> mAccessibilityStateChangeListeners =
- new CopyOnWriteArrayList<AccessibilityStateChangeListener>();
+ private final CopyOnWriteArrayList<AccessibilityStateChangeListener>
+ mAccessibilityStateChangeListeners = new CopyOnWriteArrayList<
+ AccessibilityStateChangeListener>();
+
+ private final CopyOnWriteArrayList<TouchExplorationStateChangeListener>
+ mTouchExplorationStateChangeListeners = new CopyOnWriteArrayList<
+ TouchExplorationStateChangeListener>();
/**
- * Listener for the system accessibility state. To listen for changes to the accessibility
- * state on the device, implement this interface and register it with the system by
- * calling {@link AccessibilityManager#addAccessibilityStateChangeListener
- * addAccessibilityStateChangeListener()}.
+ * Listener for the system accessibility state. To listen for changes to the
+ * accessibility state on the device, implement this interface and register
+ * it with the system by calling {@link #addAccessibilityStateChangeListener}.
*/
public interface AccessibilityStateChangeListener {
/**
- * Called back on change in the accessibility state.
+ * Called when the accessibility enabled state changes.
*
* @param enabled Whether accessibility is enabled.
*/
public void onAccessibilityStateChanged(boolean enabled);
}
+ /**
+ * Listener for the system touch exploration state. To listen for changes to
+ * the touch exploration state on the device, implement this interface and
+ * register it with the system by calling
+ * {@link #addTouchExplorationStateChangeListener}.
+ */
+ public interface TouchExplorationStateChangeListener {
+
+ /**
+ * Called when the touch exploration enabled state changes.
+ *
+ * @param enabled Whether touch exploration is enabled.
+ */
+ public void onTouchExplorationStateChanged(boolean enabled);
+ }
+
final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() {
public void setState(int state) {
mHandler.obtainMessage(DO_SET_STATE, state, 0).sendToTarget();
}
/**
- * Sets the current state.
+ * Registers a {@link TouchExplorationStateChangeListener} for changes in
+ * the global touch exploration state of the system.
*
- * @param stateFlags The state flags.
+ * @param listener The listener.
+ * @return True if successfully registered.
*/
- private void setState(int stateFlags) {
- final boolean accessibilityEnabled = (stateFlags & STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
- setAccessibilityState(accessibilityEnabled);
- mIsTouchExplorationEnabled = (stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0;
+ public boolean addTouchExplorationStateChangeListener(
+ TouchExplorationStateChangeListener listener) {
+ return mTouchExplorationStateChangeListeners.add(listener);
+ }
+
+ /**
+ * Unregisters a {@link TouchExplorationStateChangeListener}.
+ *
+ * @param listener The listener.
+ * @return True if successfully unregistered.
+ */
+ public boolean removeTouchExplorationStateChangeListener(
+ TouchExplorationStateChangeListener listener) {
+ return mTouchExplorationStateChangeListeners.remove(listener);
}
/**
- * Sets the enabled state.
+ * Sets the current state and notifies listeners, if necessary.
*
- * @param isEnabled The accessibility state.
+ * @param stateFlags The state flags.
*/
- private void setAccessibilityState(boolean isEnabled) {
+ private void setState(int stateFlags) {
+ final boolean enabled = (stateFlags & STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
+ final boolean touchExplorationEnabled =
+ (stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0;
synchronized (mHandler) {
- if (isEnabled != mIsEnabled) {
- mIsEnabled = isEnabled;
- notifyAccessibilityStateChanged();
+ final boolean wasEnabled = mIsEnabled;
+ final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
+
+ // Ensure listeners get current state from isZzzEnabled() calls.
+ mIsEnabled = enabled;
+ mIsTouchExplorationEnabled = touchExplorationEnabled;
+
+ if (wasEnabled != enabled) {
+ notifyAccessibilityStateChangedLh();
+ }
+
+ if (wasTouchExplorationEnabled != touchExplorationEnabled) {
+ notifyTouchExplorationStateChangedLh();
}
}
}
/**
* Notifies the registered {@link AccessibilityStateChangeListener}s.
+ * <p>
+ * The caller must be locked on {@link #mHandler}.
*/
- private void notifyAccessibilityStateChanged() {
+ private void notifyAccessibilityStateChangedLh() {
final int listenerCount = mAccessibilityStateChangeListeners.size();
for (int i = 0; i < listenerCount; i++) {
mAccessibilityStateChangeListeners.get(i).onAccessibilityStateChanged(mIsEnabled);
}
/**
+ * Notifies the registered {@link TouchExplorationStateChangeListener}s.
+ * <p>
+ * The caller must be locked on {@link #mHandler}.
+ */
+ private void notifyTouchExplorationStateChangedLh() {
+ final int listenerCount = mTouchExplorationStateChangeListeners.size();
+ for (int i = 0; i < listenerCount; i++) {
+ mTouchExplorationStateChangeListeners.get(i)
+ .onTouchExplorationStateChanged(mIsTouchExplorationEnabled);
+ }
+ }
+
+ /**
* Adds an accessibility interaction connection interface for a given window.
* @param windowToken The window token to which a connection is added.
* @param connection The connection.
if (ENABLED) {
final int eventType = event.getEventType();
switch (eventType) {
+ case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END:
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: {
+ // If the active window changes, clear the cache.
final int windowId = event.getWindowId();
- // If a new window, we clear the cache.
if (mWindowId != windowId) {
mWindowId = windowId;
clear();
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) {
*/
@Deprecated
public static void enablePlatformNotifications() {
- checkThread();
getFactory().getStatics().setPlatformNotificationsEnabled(true);
}
*/
@Deprecated
public static void disablePlatformNotifications() {
- checkThread();
getFactory().getStatics().setPlatformNotificationsEnabled(false);
}
* @param enabled whether to enable web contents debugging
*/
public static void setWebContentsDebuggingEnabled(boolean enabled) {
- checkThread();
getFactory().getStatics().setWebContentsDebuggingEnabled(enabled);
}
*/
@Deprecated
public static synchronized PluginList getPluginList() {
- checkThread();
return new PluginList();
}
return WebViewFactory.getProvider();
}
- private static void checkThread() {
- if (Looper.myLooper() != Looper.getMainLooper()) {
+ private final Looper mWebViewThread = Looper.myLooper();
+
+ private void checkThread() {
+ // Ignore mWebViewThread == null because this can be called during in the super class
+ // constructor, before this class's own constructor has even started.
+ if (mWebViewThread != null && Looper.myLooper() != mWebViewThread) {
Throwable throwable = new Throwable(
- "Warning: A WebView method was called on thread '" +
+ "A WebView method was called on thread '" +
Thread.currentThread().getName() + "'. " +
- "All WebView methods must be called on the UI thread. " +
- "Future versions of WebView may not support use on other threads.");
+ "All WebView methods must be called on the same thread. " +
+ "(Expected Looper " + mWebViewThread + " called on " + Looper.myLooper() +
+ ", FYI main Looper is " + Looper.getMainLooper() + ")");
Log.w(LOGTAG, Log.getStackTraceString(throwable));
StrictMode.onWebViewMethodCalledOnWrongThread(throwable);
--- /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 com.android.internal.inputmethod;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+public class InputMethodRoot extends LinearLayout {
+
+ private View mNavigationGuard;
+
+ public InputMethodRoot(Context context) {
+ this(context, null);
+ }
+
+ public InputMethodRoot(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public InputMethodRoot(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected boolean fitSystemWindows(Rect insets) {
+ if (mNavigationGuard == null) {
+ mNavigationGuard = findViewById(com.android.internal.R.id.navigationGuard);
+ }
+ if (mNavigationGuard == null) {
+ return super.fitSystemWindows(insets);
+ }
+ ViewGroup.LayoutParams lp = mNavigationGuard.getLayoutParams();
+ lp.height = insets.bottom;
+ mNavigationGuard.setLayoutParams(lp);
+ return true;
+ }
+}
// 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.
android:description="@string/permdesc_bluetoothAdmin"
android:label="@string/permlab_bluetoothAdmin" />
- <!-- Allows applications to pair bluetooth devices without user interaction -->
+ <!-- Allows applications to pair bluetooth devices without user interaction.
+ This is not available to third party applications. -->
<permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
android:permissionGroup="android.permission-group.BLUETOOTH_NETWORK"
android:protectionLevel="system|signature"
android:description="@string/permdesc_use_sip"
android:label="@string/permlab_use_sip" />
- <!-- Allows an application to request CallHandlerService implementations. -->
+ <!-- Allows an application to request CallHandlerService implementations.
+ @hide -->
<permission android:name="android.permission.BIND_CALL_SERVICE"
android:permissionGroup="android.permission-group.PHONE_CALLS"
android:protectionLevel="system|signature"
<!-- Must be required by a {@link android.nfc.cardemulation.HostApduService}
or {@link android.nfc.cardemulation.OffHostApduService} to ensure that only
- the system can bind to it. -->
+ the system can bind to it.
+ @hide -->
<permission android:name="android.permission.BIND_NFC_SERVICE"
android:label="@string/permlab_bindNfcService"
android:description="@string/permdesc_bindNfcService"
android:protectionLevel="signature" />
- <!-- Must be required by the PrintSpooler to ensure that only the system can bind to it. -->
+ <!-- Must be required by the PrintSpooler to ensure that only the system can bind to it.
+ @hide -->
<permission android:name="android.permission.BIND_PRINT_SPOOLER_SERVICE"
android:label="@string/permlab_bindPrintSpoolerService"
android:description="@string/permdesc_bindPrintSpoolerService"
android:protectionLevel="signature" />
<!-- Required to add or remove another application as a device admin.
- <p/>Not for use by third-party applications. -->
+ <p>Not for use by third-party applications.
+ @hide -->
<permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
android:label="@string/permlab_manageDeviceAdmins"
android:description="@string/permdesc_manageDeviceAdmins"
*/
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.internal.inputmethod.InputMethodRoot xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/parentPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone">
</FrameLayout>
-</LinearLayout>
+
+ <View android:id="@+id/navigationGuard"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:background="@+color/input_method_navigation_guard"/>
+</com.android.internal.inputmethod.InputMethodRoot>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
<item>"96"</item>
</string-array>
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
to enable use of the new Release 9 tables for Indic languages. -->
<!-- <integer-array name="config_sms_enabled_locking_shift_tables"></integer-array> -->
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
-
-</resources>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
+ <bool name="config_safe_media_volume_enabled">false</bool>
</resources>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
+ <bool name="config_safe_media_volume_enabled">false</bool>
</resources>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
+ <bool name="config_safe_media_volume_enabled">false</bool>
</resources>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">true</bool>
+ <bool name="config_safe_media_volume_enabled">false</bool>
</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">false</bool>
+
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">false</bool>
+
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether safe headphone volume is enabled or not (country specific). -->
+ <bool name="config_safe_media_volume_enabled">false</bool>
+
+</resources>
<drawable name="input_method_fullscreen_background">#fff9f9f9</drawable>
<drawable name="input_method_fullscreen_background_holo">@drawable/screen_background_holo_dark</drawable>
+ <color name="input_method_navigation_guard">#ff000000</color>
<!-- For date picker widget -->
<drawable name="selected_day_background">#ff0092f4</drawable>
<bool name="config_useDevInputEventForAudioJack">false</bool>
<!-- Whether safe headphone volume is enabled or not (country specific). -->
- <bool name="config_safe_media_volume_enabled">false</bool>
+ <bool name="config_safe_media_volume_enabled">true</bool>
<!-- Set to true if the wifi display supports compositing content stored
in gralloc protected buffers. For this to be true, there must exist
<java-symbol type="id" name="month" />
<java-symbol type="id" name="month_name" />
<java-symbol type="id" name="name" />
+ <java-symbol type="id" name="navigationGuard" />
<java-symbol type="id" name="next" />
<java-symbol type="id" name="next_button" />
<java-symbol type="id" name="new_app_action" />
<java-symbol type="bool" name="config_wimaxEnabled" />
<java-symbol type="bool" name="show_ongoing_ime_switcher" />
<java-symbol type="color" name="config_defaultNotificationColor" />
+ <java-symbol type="color" name="input_method_navigation_guard" />
<java-symbol type="drawable" name="ic_notification_ime_default" />
<java-symbol type="drawable" name="ic_notify_wifidisplay" />
<java-symbol type="drawable" name="ic_menu_refresh" />
return;
}
- // Create an instance of ExampleFragment
+ // Create a new Fragment to be placed in the activity layout
HeadlinesFragment firstFragment = new HeadlinesFragment();
- // In case this activity was started with special instructions from an Intent,
- // pass the Intent's extras to the fragment as arguments
+ // In case this activity was started with special instructions from an
+ // Intent, pass the Intent's extras to the fragment as arguments
firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
--- /dev/null
+page.title=Sharing Files with NFC
+
+trainingnavtop=true
+startpage=true
+
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>Dependencies and prerequisites</h2>
+<ul>
+ <li>Android 4.1 (API Level 16) or higher</li>
+ <li>At least two NFC-enabled Android devices (NFC is not supported in the emulator)</li>
+</ul>
+
+<h2>You should also read</h2>
+<ul>
+ <li>
+ <a href="{@docRoot}guide/topics/data/data-storage.html#filesExternal"
+ >Using the External Storage</a>
+ </li>
+</ul>
+
+</div>
+</div>
+
+<p>
+ Android allows you to transfer large files between devices using the Android Beam file transfer
+ feature. This feature has a simple API and allows users to start the transfer process by simply
+ touching devices. In response, Android Beam file transfer automatically copies files from one
+ device to the other and notifies the user when it's finished.
+</p>
+<p>
+ While the Android Beam file transfer API handles large amounts of data, the Android Beam NDEF
+ transfer API introduced in Android 4.0 (API level 14) handles small amounts of data such as
+ URIs or other small messages. In addition, Android Beam is only one of the features available
+ in the Android NFC framework, which allows you to read NDEF messages from NFC tags. To learn
+ more about Android Beam, see the topic
+ <a href="{@docRoot}guide/topics/connectivity/nfc/nfc.html#p2p"
+ >Beaming NDEF Messages to Other Devices</a>. To learn more about the NFC framework, see the
+ <a href="{@docRoot}guide/topics/connectivity/nfc/index.html"
+ >Near Field Communication</a> API guide.
+</p>
+<h2>Lessons</h2>
+<dl>
+ <dt>
+ <b><a href="send-files.html">Sending Files to Another Device</a></b>
+ </dt>
+ <dd>Learn how to set up your app to send files to another device.</dd>
+
+ <dt>
+ <b><a href="receive-files.html">Receiving Files from Another Device</a></b>
+ </dt>
+ <dd>
+ Learn how to set up your app to receive files sent by another device.
+ </dd>
+</dl>
+
+
--- /dev/null
+page.title=Receiving Files from Another Device
+
+trainingnavtop=true
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#IntentFilter">Respond to a Request to Display Data</a></li>
+ <li><a href="#RequestPermissions">Request File Permissions</a></li>
+ <li><a href="#GetFilePath">Get the Directory for Copied Files</a></li>
+</ol>
+<h2>You should also read</h2>
+<ul>
+ <li>
+ <a href="{@docRoot}guide/topics/providers/content-provider-basics.html#ContentURIs"
+ >Content URIs</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/components/intents-filters.html">Intents and Intent Filters</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Notifications</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/topics/data/data-storage.html#filesExternal"
+ >Using the External Storage</a>
+ </li>
+</ul>
+
+</div>
+</div>
+
+<p>
+ Android Beam file transfer copies files to a special directory on the receiving device. It also
+ scans the copied files using the Android Media Scanner and adds entries for media files to
+ the {@link android.provider.MediaStore} provider. This lesson shows you how to respond when the
+ file copy is complete, and how to locate the copied files on the receiving device.
+</p>
+<h2 id="IntentFilter">Respond to a Request to Display Data</h2>
+<p>
+ When Android Beam file transfer finishes copying files to the receiving device, it posts a
+ notification containing an {@link android.content.Intent} with the action
+ {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}, the MIME type of the first file that
+ was transferred, and a URI that points to the first file. When the user clicks the notification,
+ this intent is sent out to the system. To have your app respond to this intent, add an
+ <code><a href="{@docRoot}guide/topics/manifest/intent-filter-element.html"
+ ><intent-filter></a></code> element for the
+ <code><a href="{@docRoot}guide/topics/manifest/activity-element.html"
+ ><activity></a></code> element of the {@link android.app.Activity} that should respond.
+ In the <code><a href="{@docRoot}guide/topics/manifest/intent-filter-element.html"
+ ><intent-filter></a></code> element, add the following child elements:
+</p>
+<dl>
+ <dt>
+ <code><a href="{@docRoot}guide/topics/manifest/action-element.html"
+ ><action android:name="android.intent.action.VIEW" /></a></code>
+ </dt>
+ <dd>
+ Matches the {@link android.content.Intent#ACTION_VIEW ACTION_VIEW} intent sent from the
+ notification.
+ </dd>
+ <dt>
+ <code><a href="{@docRoot}guide/topics/manifest/category-element.html"
+ ><category android:name="android.intent.category.CATEGORY_DEFAULT" /></a></code>
+ </dt>
+ <dd>
+ Matches an {@link android.content.Intent} that doesn't have an explicit category.
+ </dd>
+ <dt>
+ <code><a href="{@docRoot}guide/topics/manifest/data-element.html"
+ ><data android:mimeType="<i>mime-type</i>" /></a></code>
+ </dt>
+ <dd>
+ Matches a MIME type. Specify only those MIME types that your app can handle.
+ </dd>
+</dl>
+<p>
+ For example, the following snippet shows you how to add an intent filter that
+ triggers the activity <code>com.example.android.nfctransfer.ViewActivity</code>:
+</p>
+<pre>
+ <activity
+ android:name="com.example.android.nfctransfer.ViewActivity"
+ android:label="Android Beam Viewer" >
+ ...
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ ...
+ </intent-filter>
+ </activity>
+</pre>
+<p class="note">
+ <strong>Note:</strong> Android Beam file transfer is not the only source of an
+ {@link android.content.Intent#ACTION_VIEW ACTION_VIEW} intent. Other apps on the receiving
+ device can also send an {@link android.content.Intent} with this action.
+ Handling this situation is discussed in the section <a href="#GetDirectory"
+ >Get the directory from a content URI</a>.
+</p>
+<h2 id="RequestPermissions">Request File Permissions</h2>
+<p>
+ To read files that Android Beam file transfer copies to the device, request the permission
+ {@link android.Manifest.permission#READ_EXTERNAL_STORAGE READ_EXTERNAL_STORAGE}. For example:
+</p>
+<pre>
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /></pre>
+<p>
+ If you want to copy transferred files to your app's own storage area, request the permission
+ {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE WRITE_EXTERNAL_STORAGE} instead.
+ {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE WRITE_EXTERNAL_STORAGE} includes
+ {@link android.Manifest.permission#READ_EXTERNAL_STORAGE READ_EXTERNAL_STORAGE}.
+</p>
+<p class="note">
+ <strong>Note:</strong> As of Android 4.2.2 (API level 17), the permission
+ {@link android.Manifest.permission#READ_EXTERNAL_STORAGE READ_EXTERNAL_STORAGE} is
+ only enforced if the user chooses to do so. Future versions of the platform may require this
+ permission in all cases. To ensure forward compatibility, request the permission now, before it
+ becomes required.
+</p>
+<p>
+ Since your app has control over its internal storage area, you don't need to request
+ write permission to copy a transferred file to your internal storage area.
+</p>
+<h2 id="GetFilePath">Get the Directory for Copied Files</h2>
+<p>
+ Android Beam file transfer copies all the files in a single transfer to one directory
+ on the receiving device. The URI in the content {@link android.content.Intent} sent by the
+ Android Beam file transfer notification points to the first transferred file. However, your
+ app may also receive an {@link android.content.Intent#ACTION_VIEW ACTION_VIEW} intent from a
+ source other than Android Beam file transfer. To determine how you should handle the incoming
+ {@link android.content.Intent}, you need to examine its scheme and authority.
+</p>
+<p>
+ To get the scheme for the URI, call {@link android.net.Uri#getScheme() Uri.getScheme()}. The
+ following code snippet shows you how to determine the scheme and handle the URI accordingly:
+</p>
+<pre>
+public class MainActivity extends Activity {
+ ...
+ // A File object containing the path to the transferred files
+ private File mParentPath;
+ // Incoming Intent
+ private Intent mIntent;
+ ...
+ /*
+ * Called from onNewIntent() for a SINGLE_TOP Activity
+ * or onCreate() for a new Activity. For onNewIntent(),
+ * remember to call setIntent() to store the most
+ * current Intent
+ *
+ */
+ private void handleViewIntent() {
+ ...
+ // Get the Intent action
+ mIntent = getIntent();
+ String action = mIntent.getAction();
+ /*
+ * For ACTION_VIEW, the Activity is being asked to display data.
+ * Get the URI.
+ */
+ if (TextUtils.equals(action, Intent.ACTION_VIEW)) {
+ // Get the URI from the Intent
+ Uri beamUri = mIntent.getData();
+ /*
+ * Test for the type of URI, by getting its scheme value
+ */
+ if (TextUtils.equals(beamUri.getScheme(), "file")) {
+ mParentPath = handleFileUri(beamUri);
+ } else if (TextUtils.equals(
+ beamUri.getScheme(), "content")) {
+ mParentPath = handleContentUri(beamUri);
+ }
+ }
+ ...
+ }
+ ...
+}
+</pre>
+<h3>Get the directory from a file URI</h3>
+<p>
+ If the incoming {@link android.content.Intent} contains a file URI, the URI contains the
+ absolute file name of a file, including the full directory path and file name. For Android Beam
+ file transfer, the directory path points to the location of the other transferred files, if
+ any. To get the directory path, get the path part of the URI, which contains all of the URI
+ except the <code>file:</code> prefix. Create a {@link java.io.File} from the path part, then
+ get the parent path of the {@link java.io.File}:
+</p>
+<pre>
+ ...
+ public String handleFileUri(Uri beamUri) {
+ // Get the path part of the URI
+ String fileName = beamUri.getPath();
+ // Create a File object for this filename
+ File copiedFile = new File(fileName);
+ // Get a string containing the file's parent directory
+ return copiedFile.getParent();
+ }
+ ...
+</pre>
+
+<h3 id="GetDirectory">Get the directory from a content URI</h3>
+<p>
+ If the incoming {@link android.content.Intent} contains a content URI, the URI may point to a
+ directory and file name stored in the {@link android.provider.MediaStore} content provider. You
+ can detect a content URI for {@link android.provider.MediaStore} by testing the URI's
+ authority value. A content URI for {@link android.provider.MediaStore} may come from
+ Android Beam file transfer or from another app, but in both cases you can retrieve a directory
+ and file name for the content URI.
+</p>
+<p>
+ You can also receive an incoming {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}
+ intent containing a content URI for a content provider other than
+ {@link android.provider.MediaStore}. In this case, the content URI doesn't contain the
+ {@link android.provider.MediaStore} authority value, and the content URI usually doesn't point
+ to a directory.
+</p>
+<p class="note">
+ <strong>Note:</strong> For Android Beam file transfer, you receive a content URI in the
+ {@link android.content.Intent#ACTION_VIEW ACTION_VIEW} intent if the first incoming file
+ has a MIME type of "audio/*", "image/*", or "video/*", indicating that the file is media-
+ related. Android Beam file transfer indexes the media files it transfers by running Media
+ Scanner on the directory where it stores transferred files. Media Scanner writes its results
+ to the {@link android.provider.MediaStore} content provider, then it passes a content URI
+ for the first file back to Android Beam file transfer. This content URI is the one you
+ receive in the notification {@link android.content.Intent}. To get the directory
+ of the first file, you retrieve it from {@link android.provider.MediaStore} using the content
+ URI.
+</p>
+<h3>Determine the content provider</h3>
+<p>
+ To determine if you can retrieve a file directory from the content URI, determine the
+ the content provider associated with the URI by calling
+ {@link android.net.Uri#getAuthority Uri.getAuthority()} to get the URI's authority. The
+ result has two possible values:
+</p>
+<dl>
+ <dt>
+ {@link android.provider.MediaStore#AUTHORITY MediaStore.AUTHORITY}
+ </dt>
+ <dd>
+ The URI is for a file or files tracked by {@link android.provider.MediaStore}. Retrieve the
+ full file name from {@link android.provider.MediaStore}, and get directory from the file
+ name.
+ </dd>
+ <dt>
+ Any other authority value
+ </dt>
+ <dd>
+ A content URI from another content provider. Display the data associated with the content
+ URI, but don't get the file directory.
+ </dd>
+</dl>
+<p>
+ To get the directory for a {@link android.provider.MediaStore} content URI,
+ run a query that specifies the incoming content URI for the {@link android.net.Uri} argument and
+ the column {@link android.provider.MediaStore.MediaColumns#DATA MediaColumns.DATA} for the
+ projection. The returned {@link android.database.Cursor} contains the full path and name for
+ the file represented by the URI. This path also contains all the other files that Android Beam
+ file transfer just copied to the device.
+</p>
+<p>
+ The following snippet shows you how to test the authority of the content URI and retrieve the
+ the path and file name for the transferred file:
+</p>
+<pre>
+ ...
+ public String handleContentUri(Uri beamUri) {
+ // Position of the filename in the query Cursor
+ int filenameIndex;
+ // File object for the filename
+ File copiedFile;
+ // The filename stored in MediaStore
+ String fileName;
+ // Test the authority of the URI
+ if (!TextUtils.equals(beamUri.getAuthority(), MediaStore.AUTHORITY)) {
+ /*
+ * Handle content URIs for other content providers
+ */
+ // For a MediaStore content URI
+ } else {
+ // Get the column that contains the file name
+ String[] projection = { MediaStore.MediaColumns.DATA };
+ Cursor pathCursor =
+ getContentResolver().query(beamUri, projection,
+ null, null, null);
+ // Check for a valid cursor
+ if (pathCursor != null &&
+ pathCursor.moveToFirst()) {
+ // Get the column index in the Cursor
+ filenameIndex = pathCursor.getColumnIndex(
+ MediaStore.MediaColumns.DATA);
+ // Get the full file name including path
+ fileName = pathCursor.getString(filenameIndex);
+ // Create a File object for the filename
+ copiedFile = new File(fileName);
+ // Return the parent directory of the file
+ return new File(copiedFile.getParent());
+ } else {
+ // The query didn't work; return null
+ return null;
+ }
+ }
+ }
+ ...
+</pre>
+<p>
+ To learn more about retrieving data from a content provider, see the section
+ <a href="{@docRoot}guide/topics/providers/content-provider-basics.html#SimpleQuery"
+ >Retrieving Data from the Provider</a>.
+</p>
--- /dev/null
+page.title=Sending Files to Another Device
+
+trainingnavtop=true
+@jd:body
+
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#DeclareFeatures">Declare Features in the Manifest</a>
+ <li><a href="#TestAndroidBeam">Test for Android Beam File Transfer Support</a></li>
+ <li>
+ <a href="#CreateCallback"
+ >Create a Callback Method That Provides Files</a>
+ </li>
+ <li><a href="#ProvideUri">Specify the Files to Send</a>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}guide/topics/data/data-storage.html">Storage Options</a></li>
+</ul>
+
+</div>
+</div>
+<p>
+ This lesson shows you how to design your app to send large files to another device using
+ Android Beam file transfer. To send files, you request permission to use NFC and external
+ storage, test to ensure your device supports NFC, and provide URIs to Android Beam file
+ transfer.
+</p>
+<p>
+ The Android Beam file transfer feature has the following requirements:
+</p>
+<ol>
+ <li>
+ Android Beam file transfer for large files is only available in Android 4.1 (API level 16)
+ and higher.
+ </li>
+ <li>
+ Files you want to transfer must reside in external storage. To learn more about using
+ external storage, read <a href="{@docRoot}guide/topics/data/data-storage.html#filesExternal"
+ >Using the External Storage</a>.
+ </li>
+ <li>
+ Each file you want to transfer must be world-readable. You can set this permission by
+ calling the method {@link java.io.File#setReadable File.setReadable(true,false)}.
+ </li>
+ <li>
+ You must provide a file URI for the files you want to transfer. Android Beam file transfer
+ is unable to handle content URIs generated by
+ {@link android.support.v4.content.FileProvider#getUriForFile FileProvider.getUriForFile}.
+ </li>
+</ol>
+
+<h2 id="DeclareFeatures">Declare Features in the Manifest</h2>
+<p>
+ First, edit your app manifest to declare the permissions and features your app needs.
+</p>
+<h3>Request Permissions</h3>
+<p>
+ To allow your app to use Android Beam file transfer to send files from external storage using
+ NFC, you must request the following permissions in your app manifest:
+</p>
+<dl>
+ <dt>
+ {@link android.Manifest.permission#NFC NFC}
+ </dt>
+ <dd>
+ Allows your app to send data over NFC. To specify this permission, add the following element
+ as a child of the <code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"
+ ><manifest></a></code> element:
+<pre>
+ <uses-permission android:name="android.permission.NFC" />
+</pre>
+ </dd>
+ <dt>
+ {@link android.Manifest.permission#READ_EXTERNAL_STORAGE READ_EXTERNAL_STORAGE}
+ </dt>
+ <dd>
+ Allows your app to read from external storage. To specify this permission, add the following
+ element as a child of the
+ <code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"
+ ><manifest></a></code> element:
+<pre>
+ <uses-permission
+ android:name="android.permission.READ_EXTERNAL_STORAGE" />
+</pre>
+ <p class="note">
+ <strong>Note:</strong> As of Android 4.2.2 (API level 17), this permission is not
+ enforced. Future versions of the platform may require it for apps that want to read from
+ external storage. To ensure forward compatibility, request the permission now, before it
+ becomes required.
+ </p>
+ </dd>
+</dl>
+<h3>Specify the NFC feature</h3>
+<p>
+ Specify that your app uses NFC, by adding a
+ <code><a href="{@docRoot}guide/topics/manifest/uses-feature-element.html"
+ ><uses-feature></a></code> element as a child
+ of the <code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"
+ ><manifest></a></code> element. Set the <code>android:required</code> attribute to
+ <code>true</code> to indicate that your app won't function unless NFC is present.
+</p>
+<p>
+ The following snippet shows you how to specify the
+ <code><a href="{@docRoot}guide/topics/manifest/uses-feature-element.html"
+ ><uses-feature></a></code> element:
+</p>
+<pre>
+<uses-feature
+ android:name="android.hardware.nfc"
+ android:required="true" /></pre>
+<p>
+ Note that if your app only uses NFC as an option, but still functions if NFC isn't present, you
+ should set <code>android:required</code> to <code>false</code>, and test for NFC in code.
+</p>
+<h3>Specify Android Beam file transfer</h3>
+<p>
+ Since Android Beam file transfer is only available in Android 4.1 (API level 16) and later,
+ if your app depends on Android Beam file transfer for a key part of its functionality you must
+ specify the <code><a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"
+ ><uses-sdk></a></code> element with the
+ <code><a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min"
+ >android:minSdkVersion</a>="16"</code> attribute. Otherwise, you can set
+ <code><a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min"
+ >android:minSdkVersion</a></code> to another value as necessary, and test for the platform
+ version in code, as described in the following section.
+</p>
+<h2 id="TestAndroidBeam">Test for Android Beam File Transfer Support</h2>
+<p>
+ To specify in your app manifest that NFC is optional, you use the following element:
+</p>
+<pre>
+<uses-feature android:name="android.hardware.nfc" android:required="false" /></pre>
+<p>
+ If you set the attribute
+ <code><a href="guide/topics/manifest/uses-feature-element.html#required"
+ >android:required</a>="false"</code>, you must test for NFC support and Android Beam file
+ transfer support in code.
+</p>
+<p>
+ To test for Android Beam file transfer support in code, start by testing that the device
+ supports NFC by calling {@link android.content.pm.PackageManager#hasSystemFeature
+ PackageManager.hasSystemFeature()} with the argument
+ {@link android.content.pm.PackageManager#FEATURE_NFC FEATURE_NFC}. Next, check that the Android
+ version supports Android Beam file transfer by testing the value of
+ {@link android.os.Build.VERSION#SDK_INT}. If Android Beam file transfer is supported, get an
+ instance of the NFC controller, which allows you to communicate with the NFC hardware.
+ For example:
+</p>
+<pre>
+public class MainActivity extends Activity {
+ ...
+ NfcAdapter mNfcAdapter;
+ // Flag to indicate that Android Beam is available
+ boolean mAndroidBeamAvailable = false;
+ ...
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ ...
+ // NFC isn't available on the device
+ if (!PackageManager.hasSystemFeature(PackageManager.FEATURE_NFC)) {
+ /*
+ * Disable NFC features here.
+ * For example, disable menu items or buttons that activate
+ * NFC-related features
+ */
+ ...
+ // Android Beam file transfer isn't supported
+ } else if (Build.VERSION.SDK_INT <
+ Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ // If Android Beam isn't available, don't continue.
+ mAndroidBeamAvailable = false;
+ /*
+ * Disable Android Beam file transfer features here.
+ */
+ ...
+ // Android Beam file transfer is available, continue
+ } else {
+ mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ ...
+ }
+ }
+ ...
+}</pre>
+
+<h2 id="CreateCallback">
+ Create a Callback Method that Provides Files
+</h2>
+<p>
+ Once you've verified that the device supports Android Beam file transfer, add a callback
+ method that the system invokes when Android Beam file transfer detects that the user wants
+ to send files to another NFC-enabled device. In this callback method, return an array of
+ {@link android.net.Uri} objects. Android Beam file transfer copies the files represented by
+ these URIs to the receiving device.
+</p>
+<p>
+ To add the callback method, implement the
+ {@link android.nfc.NfcAdapter.CreateBeamUrisCallback} interface and its method
+ {@link android.nfc.NfcAdapter.CreateBeamUrisCallback#createBeamUris createBeamUris()}. The
+ following snippet shows you how to do this:
+</p>
+<pre>
+public class MainActivity extends Activity {
+ ...
+ // List of URIs to provide to Android Beam
+ private Uri[] mFileUris = new Uri[10];
+ ...
+ /**
+ * Callback that Android Beam file transfer calls to get
+ * files to share
+ */
+ private class FileUriCallback implements
+ NfcAdapter.CreateBeamUrisCallback {
+ public FileUriCallback() {
+ }
+ /**
+ * Create content URIs as needed to share with another device
+ */
+ @Override
+ public Uri[] createBeamUris(NfcEvent event) {
+ return mFileUris;
+ }
+ }
+ ...
+}
+</pre>
+<p>
+ Once you've implemented the interface, provide the callback to Android Beam file transfer by
+ calling {@link android.nfc.NfcAdapter#setBeamPushUrisCallback setBeamPushUrisCallback()}. The
+ following snippet shows you how to do this:
+</p>
+<pre>
+public class MainActivity extends Activity {
+ ...
+ // Instance that returns available files from this app
+ private FileUriCallback mFileUriCallback;
+ ...
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ ...
+ // Android Beam file transfer is available, continue
+ ...
+ mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ /*
+ * Instantiate a new FileUriCallback to handle requests for
+ * URIs
+ */
+ mFileUriCallback = new FileUriCallback();
+ // Set the dynamic callback for URI requests.
+ mNfcAdapter.setBeamPushUrisCallback(mFileUriCallback,this);
+ ...
+ }
+ ...
+}
+</pre>
+<p class="note">
+ <strong>Note:</strong> You can also provide the array of {@link android.net.Uri} objects
+ directly to the NFC framework through your app's {@link android.nfc.NfcAdapter} instance. Choose
+ this approach if you can define the URIs to transfer before the NFC touch event occurs.
+ To learn more about this approach, see {@link android.nfc.NfcAdapter#setBeamPushUris
+ NfcAdapter.setBeamPushUris()}.
+</p>
+<h2 id="ProvideUri">Specify the Files to Send</h2>
+<p>
+ To transfer one or more files to another NFC-enabled device, get a file URI (a URI with a
+ <code>file</code> scheme) for each file and then add the URI to an array of
+ {@link android.net.Uri} objects. To transfer a file, you must also have permanent read access
+ for the file. For example, the following snippet shows you how to get a file URI from a file
+ name and then add the URI to the array:
+</p>
+<pre>
+ /*
+ * Create a list of URIs, get a File,
+ * and set its permissions
+ */
+ private Uri[] mFileUris = new Uri[10];
+ String transferFile = "transferimage.jpg";
+ File extDir = getExternalFilesDir(null);
+ File requestFile = new File(extDir, transferFile);
+ requestFile.setReadable(true, false);
+ // Get a URI for the File and add it to the list of URIs
+ fileUri = Uri.fromFile(requestFile);
+ if (fileUri != null) {
+ mFileUris[0] = fileUri;
+ } else {
+ Log.e("My Activity", "No File URI available for file.");
+ }
+</pre>
</li>
</ul>
</li>
-
+ <li class="nav-section">
+ <div class="nav-section-header">
+ <a href="<?cs var:toroot ?>training/beam-files/index.html"
+ description=
+ "How to transfer files between devices using the NFC Android Beam feature."
+ >Sharing Files with NFC</a>
+ </div>
+ <ul>
+ <li>
+ <a href="<?cs var:toroot ?>training/beam-files/send-files.html"
+ >Sending Files to Another Device</a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/beam-files/receive-files.html"
+ >Receiving Files from Another Device</a></li>
+ </ul>
+ </li>
<li class="nav-section">
<div class="nav-section-header">
<a href="<?cs var:toroot ?>training/basics/network-ops/index.html"
"How to design a robust conflict resolution strategy for apps that save data to the cloud."
>Resolving Cloud Save Conflicts
</a>
- </li>
+ </li>
</li>
<li class="nav-section">
<div class="nav-section-header">
</a>
</div>
<ul>
-
<li>
<a href="<?cs var:toroot ?>training/articles/security-tips.html"
description=
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*/);
}
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();
}
}
}
@Override
protected Void doInBackground(Void... params) {
// Restore last stack for calling package
- final String packageName = getCallingPackage();
+ final String packageName = getCallingPackageMaybeExtra();
final Cursor cursor = getContentResolver()
.query(RecentsProvider.buildResume(packageName), null, null, null, null);
try {
return mState.stack.peek();
}
+ private String getCallingPackageMaybeExtra() {
+ final String extra = getIntent().getStringExtra(DocumentsContract.EXTRA_PACKAGE_NAME);
+ return (extra != null) ? extra : getCallingPackage();
+ }
+
public Executor getCurrentExecutor() {
final DocumentInfo cwd = getCurrentDirectory();
if (cwd != null && cwd.authority != null) {
if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
// Remember that we last picked via external app
- final String packageName = getCallingPackage();
+ final String packageName = getCallingPackageMaybeExtra();
final ContentValues values = new ContentValues();
values.put(ResumeColumns.EXTERNAL, 1);
getContentResolver().insert(RecentsProvider.buildResume(packageName), values);
}
// Remember location for next app launch
- final String packageName = getCallingPackage();
+ final String packageName = getCallingPackageMaybeExtra();
values.clear();
values.put(ResumeColumns.STACK, rawStack);
values.put(ResumeColumns.EXTERNAL, 0);
handleDocumentsProvider(info.providerInfo);
}
- // Pick up legacy providers
- final List<ProviderInfo> legacyProviders = pm.queryContentProviders(
- null, -1, PackageManager.GET_META_DATA);
- for (ProviderInfo info : legacyProviders) {
- if (info.metaData != null && info.metaData.containsKey(
- DocumentsContract.META_DATA_DOCUMENT_PROVIDER)) {
- handleDocumentsProvider(info);
- }
- }
-
final long delta = SystemClock.elapsedRealtime() - start;
Log.d(TAG, "Update found " + mTaskRoots.size() + " roots in " + delta + "ms");
synchronized (mLock) {
--- /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>
+ <!-- Carriers in this locale are sensitive to capitalization of carrier text.
+ This makes the entire interface consistent by switching back to normal case. -->
+ <bool name="kg_use_all_caps">false</bool>
+</resources>
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Color;
+import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
private final WindowManager mWindowManager;
private final Point mRenderedSize = new Point();
private final int[] mTmpLoc = new int[2];
- private final Rect mTmpRect = new Rect();
private long mLaunchCameraStart;
private boolean mActive;
private boolean mTransitioning;
private boolean mDown;
+ private final Rect mInsets = new Rect();
+
private FixedSizeFrameLayout mPreview;
private View mFullscreenPreview;
+ private View mFakeNavBar;
private final Runnable mTransitionToCameraRunnable = new Runnable() {
@Override
private void render() {
final View root = getRootView();
- final int width = root.getWidth();
- final int height = root.getHeight();
+ final int width = root.getWidth() - mInsets.right; // leave room
+ final int height = root.getHeight() - mInsets.bottom; // for bars
if (mRenderedSize.x == width && mRenderedSize.y == height) {
- if (DEBUG) Log.d(TAG, String.format("Already rendered at size=%sx%s", width, height));
+ if (DEBUG) Log.d(TAG, String.format("Already rendered at size=%sx%s %d%%",
+ width, height, (int)(100*mPreview.getScaleX())));
return;
}
if (width == 0 || height == 0) {
mPreview.setTranslationY(pvTransY);
mRenderedSize.set(width, height);
- if (DEBUG) Log.d(TAG, String.format("Rendered camera widget size=%sx%s instance=%s",
- width, height, instanceId()));
+ if (DEBUG) Log.d(TAG, String.format("Rendered camera widget size=%sx%s %d%% instance=%s",
+ width, height, (int)(100*mPreview.getScaleX()), instanceId()));
}
private void transitionToCamera() {
enableWindowExitAnimation(false);
+ final int navHeight = mInsets.bottom;
+ final int navWidth = mInsets.right;
+
mPreview.getLocationInWindow(mTmpLoc);
final float pvHeight = mPreview.getHeight() * mPreview.getScaleY();
final float pvCenter = mTmpLoc[1] + pvHeight / 2f;
final ViewGroup root = (ViewGroup) getRootView();
+
+ if (DEBUG) {
+ Log.d(TAG, "root = " + root.getLeft() + "," + root.getTop() + " "
+ + root.getWidth() + "x" + root.getHeight());
+ }
+
if (mFullscreenPreview == null) {
mFullscreenPreview = getPreviewWidget(mContext, mWidgetInfo);
mFullscreenPreview.setClickable(false);
- root.addView(mFullscreenPreview);
+ root.addView(mFullscreenPreview, new FrameLayout.LayoutParams(
+ root.getWidth() - navWidth,
+ root.getHeight() - navHeight));
}
- root.getWindowVisibleDisplayFrame(mTmpRect);
- final float fsHeight = mTmpRect.height();
- final float fsCenter = mTmpRect.top + fsHeight / 2;
+ final float fsHeight = root.getHeight() - navHeight;
+ final float fsCenter = root.getTop() + fsHeight / 2;
- final float fsScaleY = pvHeight / fsHeight;
+ final float fsScaleY = mPreview.getScaleY();
final float fsTransY = pvCenter - fsCenter;
- final float fsScaleX = mPreview.getScaleX();
+ final float fsScaleX = fsScaleY;
mPreview.setVisibility(View.GONE);
mFullscreenPreview.setVisibility(View.VISIBLE);
.setDuration(WIDGET_ANIMATION_DURATION)
.withEndAction(mPostTransitionToCameraEndAction)
.start();
+
+ if (navHeight > 0 || navWidth > 0) {
+ final boolean atBottom = navHeight > 0;
+ if (mFakeNavBar == null) {
+ mFakeNavBar = new View(mContext);
+ mFakeNavBar.setBackgroundColor(Color.BLACK);
+ root.addView(mFakeNavBar, new FrameLayout.LayoutParams(
+ atBottom ? FrameLayout.LayoutParams.MATCH_PARENT
+ : navWidth,
+ atBottom ? navHeight
+ : FrameLayout.LayoutParams.MATCH_PARENT,
+ atBottom ? Gravity.BOTTOM|Gravity.FILL_HORIZONTAL
+ : Gravity.RIGHT|Gravity.FILL_VERTICAL));
+ mFakeNavBar.setPivotY(navHeight);
+ mFakeNavBar.setPivotX(navWidth);
+ }
+ mFakeNavBar.setAlpha(0f);
+ if (atBottom) {
+ mFakeNavBar.setScaleY(0.5f);
+ } else {
+ mFakeNavBar.setScaleX(0.5f);
+ }
+ mFakeNavBar.setVisibility(View.VISIBLE);
+ mFakeNavBar.animate()
+ .alpha(1f)
+ .scaleY(1f)
+ .scaleY(1f)
+ .setDuration(WIDGET_ANIMATION_DURATION)
+ .start();
+ }
mCallbacks.onLaunchingCamera();
}
mFullscreenPreview.animate().cancel();
mFullscreenPreview.setVisibility(View.GONE);
}
+ if (mFakeNavBar != null) {
+ mFakeNavBar.animate().cancel();
+ mFakeNavBar.setVisibility(View.GONE);
+ }
enableWindowExitAnimation(true);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (DEBUG) Log.d(TAG, String.format("onSizeChanged new=%sx%s old=%sx%s at %s",
w, h, oldw, oldh, SystemClock.uptimeMillis()));
+ if ((w != oldw && oldw > 0) || (h != oldh && oldh > 0)) {
+ // we can't trust the old geometry anymore; force a re-render
+ mRenderedSize.x = mRenderedSize.y = -1;
+ }
mHandler.post(mRenderRunnable);
super.onSizeChanged(w, h, oldw, oldh);
}
private String instanceId() {
return Integer.toHexString(hashCode());
}
+
+ public void setInsets(Rect insets) {
+ if (DEBUG) Log.d(TAG, "setInsets: " + insets);
+ mInsets.set(insets);
+ }
}
mInsets.set(insets);
if (mSlidingChallengeLayout != null) mSlidingChallengeLayout.setInsets(mInsets);
if (mMultiPaneChallengeLayout != null) mMultiPaneChallengeLayout.setInsets(mInsets);
+
+ final CameraWidgetFrame cameraWidget = findCameraPage();
+ if (cameraWidget != null) cameraWidget.setInsets(mInsets);
}
@Override
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);
}
--- /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>
+
+ <string-array name="pdf_printer_media_sizes" translatable="false">
+ <item>NA_LETTER</item>
+ <item>NA_GOVT_LETTER</item>
+ <item>NA_LEGAL</item>
+ <item>NA_JUNIOR_LEGAL</item>
+ <item>NA_LEDGER</item>
+ <item>NA_TABLOID</item>
+ <item>NA_INDEX_3X5</item>
+ <item>NA_INDEX_4X6</item>
+ <item>NA_INDEX_5X8</item>
+ <item>NA_MONARCH</item>
+ <item>NA_QUARTO</item>
+ <item>NA_FOOLSCAP</item>
+ </string-array>
+
+</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>
+
+ <string-array name="pdf_printer_media_sizes" translatable="false">
+ <item>NA_LETTER</item>
+ <item>NA_GOVT_LETTER</item>
+ <item>NA_LEGAL</item>
+ <item>NA_JUNIOR_LEGAL</item>
+ <item>NA_LEDGER</item>
+ <item>NA_TABLOID</item>
+ <item>NA_INDEX_3X5</item>
+ <item>NA_INDEX_4X6</item>
+ <item>NA_INDEX_5X8</item>
+ <item>NA_MONARCH</item>
+ <item>NA_QUARTO</item>
+ <item>NA_FOOLSCAP</item>
+ </string-array>
+
+</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>
+
+ <string name="mediasize_default">NA_LETTER</string>
+ <string name="mediasize_standard">@string/mediasize_standard_north_america</string>
+
+</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>
+
+ <string-array name="pdf_printer_media_sizes" translatable="false">
+ <item>NA_LETTER</item>
+ <item>NA_GOVT_LETTER</item>
+ <item>NA_LEGAL</item>
+ <item>NA_JUNIOR_LEGAL</item>
+ <item>NA_LEDGER</item>
+ <item>NA_TABLOID</item>
+ <item>NA_INDEX_3X5</item>
+ <item>NA_INDEX_4X6</item>
+ <item>NA_INDEX_5X8</item>
+ <item>NA_MONARCH</item>
+ <item>NA_QUARTO</item>
+ <item>NA_FOOLSCAP</item>
+ </string-array>
+
+</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>
+
+ <string-array name="pdf_printer_media_sizes" translatable="false">
+ <item>JIS_B10</item>
+ <item>JIS_B9</item>
+ <item>JIS_B8</item>
+ <item>JIS_B7</item>
+ <item>JIS_B6</item>
+ <item>JIS_B5</item>
+ <item>JIS_B4</item>
+ <item>JIS_B3</item>
+ <item>JIS_B2</item>
+ <item>JIS_B1</item>
+ <item>JIS_B0</item>
+ <item>JIS_EXEC</item>
+ <item>JPN_CHOU4</item>
+ <item>JPN_CHOU3</item>
+ <item>JPN_CHOU2</item>
+ <item>JPN_HAGAKI</item>
+ <item>JPN_OUFUKU</item>
+ <item>JPN_KAHU</item>
+ <item>JPN_KAKU2</item>
+ <item>JPN_YOU4</item>
+ </string-array>
+
+</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>
+
+ <string-array name="pdf_printer_media_sizes" translatable="false">
+ <item>ROC_8K</item>
+ <item>ROC_16K</item>
+ <item>PRC_1</item>
+ <item>PRC_2</item>
+ <item>PRC_3</item>
+ <item>PRC_4</item>
+ <item>PRC_5</item>
+ <item>PRC_6</item>
+ <item>PRC_7</item>
+ <item>PRC_8</item>
+ <item>PRC_9</item>
+ <item>PRC_10</item>
+ <item>PRC_16K</item>
+ <item>OM_PA_KAI</item>
+ <item>OM_DAI_PA_KAI</item>
+ <item>OM_JUURO_KU_KAI</item>
+ </string-array>
+
+</resources>
<!-- Template for the notificaiton label for a blocked print job. [CHAR LIMIT=25] -->
<string name="blocked_notification_title_template">Printer blocked <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
+ <!-- Template for the notificaiton label for a composite (multiple items) print jobs notification. [CHAR LIMIT=25] -->
+ <plurals name="composite_notification_title_template">
+ <item quantity="one"><xliff:g id="print_job_name" example="foo.jpg">%1$d</xliff:g> print job</item>
+ <item quantity="other"><xliff:g id="print_job_name" example="foo.jpg">%1$d</xliff:g> print jobs</item>
+ </plurals>
+
<!-- Label for the notification button for cancelling a print job. [CHAR LIMIT=25] -->
<string name="cancel">Cancel</string>
package com.android.printspooler;
import android.app.Notification;
+import android.app.Notification.InboxStyle;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.Uri;
import android.os.AsyncTask;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.provider.Settings;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* This class is responsible for updating the print notifications
* based on print job state transitions.
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
}
- public void onPrintJobStateChanged(PrintJobInfo printJob) {
- if (DEBUG) {
- Log.i(LOG_TAG, "onPrintJobStateChanged() printJobId: "
- + printJob.getId().flattenToString() + " state:"
- + PrintJobInfo.stateToString(printJob.getState()));
+ public void onUpdateNotifications(List<PrintJobInfo> printJobs) {
+ List<PrintJobInfo> notifyPrintJobs = new ArrayList<PrintJobInfo>();
+
+ final int printJobCount = printJobs.size();
+ for (int i = 0; i < printJobCount; i++) {
+ PrintJobInfo printJob = printJobs.get(i);
+ if (shouldNotifyForState(printJob.getState())) {
+ notifyPrintJobs.add(printJob);
+ }
}
- switch (printJob.getState()) {
- case PrintJobInfo.STATE_QUEUED:
- case PrintJobInfo.STATE_STARTED: {
- createPrintingNotification(printJob);
- } break;
+ updateNotification(notifyPrintJobs);
+ }
+
+ private void updateNotification(List<PrintJobInfo> printJobs) {
+ if (printJobs.size() <= 0) {
+ removeNotification();
+ } else if (printJobs.size() == 1) {
+ createSimpleNotification(printJobs.get(0));
+ } else {
+ createStackedNotification(printJobs);
+ }
+ }
+
+ private void createSimpleNotification(PrintJobInfo printJob) {
+ switch (printJob.getState()) {
case PrintJobInfo.STATE_FAILED: {
createFailedNotification(printJob);
} break;
- case PrintJobInfo.STATE_COMPLETED:
- case PrintJobInfo.STATE_CANCELED: {
- removeNotification(printJob.getId());
+ case PrintJobInfo.STATE_BLOCKED: {
+ if (!printJob.isCancelling()) {
+ createBlockedNotification(printJob);
+ } else {
+ createCancellingNotification(printJob);
+ }
} break;
- case PrintJobInfo.STATE_BLOCKED: {
- createBlockedNotification(printJob);
+ default: {
+ if (!printJob.isCancelling()) {
+ createPrintingNotification(printJob);
+ } else {
+ createCancellingNotification(printJob);
+ }
} break;
}
}
private void createPrintingNotification(PrintJobInfo printJob) {
Notification.Builder builder = new Notification.Builder(mContext)
.setContentIntent(createContentIntent(printJob.getId()))
- .setSmallIcon(com.android.internal.R.drawable.ic_print)
- .setContentTitle(mContext.getString(R.string.printing_notification_title_template,
- printJob.getLabel()))
+ .setSmallIcon(computeNotificationIcon(printJob))
+ .setContentTitle(computeNotificationTitle(printJob))
.addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel),
createCancelIntent(printJob))
.setContentText(printJob.getPrinterName())
.setWhen(System.currentTimeMillis())
.setOngoing(true)
.setShowWhen(true);
- mNotificationManager.notify(printJob.getId().flattenToString(), 0, builder.build());
+ mNotificationManager.notify(0, builder.build());
}
private void createFailedNotification(PrintJobInfo printJob) {
Notification.Builder builder = new Notification.Builder(mContext)
.setContentIntent(createContentIntent(printJob.getId()))
- .setSmallIcon(com.android.internal.R.drawable.ic_print_error)
- .setContentTitle(mContext.getString(R.string.failed_notification_title_template,
- printJob.getLabel()))
+ .setSmallIcon(computeNotificationIcon(printJob))
+ .setContentTitle(computeNotificationTitle(printJob))
.addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel),
createCancelIntent(printJob))
.addAction(R.drawable.ic_restart, mContext.getString(R.string.restart),
.setWhen(System.currentTimeMillis())
.setOngoing(true)
.setShowWhen(true);
- mNotificationManager.notify(printJob.getId().flattenToString(), 0, builder.build());
+ mNotificationManager.notify(0, builder.build());
}
private void createBlockedNotification(PrintJobInfo printJob) {
Notification.Builder builder = new Notification.Builder(mContext)
.setContentIntent(createContentIntent(printJob.getId()))
- .setSmallIcon(com.android.internal.R.drawable.ic_print_error)
- .setContentTitle(mContext.getString(R.string.blocked_notification_title_template,
- printJob.getLabel()))
+ .setSmallIcon(computeNotificationIcon(printJob))
+ .setContentTitle(computeNotificationTitle(printJob))
.addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel),
createCancelIntent(printJob))
.setContentText(printJob.getPrinterName())
.setWhen(System.currentTimeMillis())
.setOngoing(true)
.setShowWhen(true);
- mNotificationManager.notify(printJob.getId().flattenToString(), 0, builder.build());
+ mNotificationManager.notify(0, builder.build());
}
- private void removeNotification(PrintJobId printJobId) {
- mNotificationManager.cancel(printJobId.flattenToString(), 0);
+ private void createCancellingNotification(PrintJobInfo printJob) {
+ Notification.Builder builder = new Notification.Builder(mContext)
+ .setContentIntent(createContentIntent(printJob.getId()))
+ .setSmallIcon(computeNotificationIcon(printJob))
+ .setContentTitle(computeNotificationTitle(printJob))
+ .setContentText(printJob.getPrinterName())
+ .setWhen(System.currentTimeMillis())
+ .setOngoing(true)
+ .setShowWhen(true);
+ mNotificationManager.notify(0, builder.build());
+ }
+
+ private void createStackedNotification(List<PrintJobInfo> printJobs) {
+ Notification.Builder builder = new Notification.Builder(mContext)
+ .setContentIntent(createContentIntent(null))
+ .setWhen(System.currentTimeMillis())
+ .setOngoing(true)
+ .setShowWhen(true);
+
+ final int printJobCount = printJobs.size();
+
+ InboxStyle inboxStyle = new InboxStyle();
+ inboxStyle.setBigContentTitle(String.format(mContext.getResources().getQuantityText(
+ R.plurals.composite_notification_title_template,
+ printJobCount).toString(), printJobCount));
+
+ for (int i = printJobCount - 1; i>= 0; i--) {
+ PrintJobInfo printJob = printJobs.get(i);
+ if (i == printJobCount - 1) {
+ builder.setLargeIcon(((BitmapDrawable) mContext.getResources().getDrawable(
+ computeNotificationIcon(printJob))).getBitmap());
+ builder.setSmallIcon(com.android.internal.R.drawable.ic_print);
+ builder.setContentTitle(computeNotificationTitle(printJob));
+ builder.setContentText(printJob.getPrinterName());
+ }
+ inboxStyle.addLine(computeNotificationTitle(printJob));
+ }
+
+ builder.setNumber(printJobCount);
+ builder.setStyle(inboxStyle);
+
+ mNotificationManager.notify(0, builder.build());
+ }
+
+ private String computeNotificationTitle(PrintJobInfo printJob) {
+ switch (printJob.getState()) {
+ case PrintJobInfo.STATE_FAILED: {
+ return mContext.getString(R.string.failed_notification_title_template,
+ printJob.getLabel());
+ }
+
+ case PrintJobInfo.STATE_BLOCKED: {
+ if (!printJob.isCancelling()) {
+ return mContext.getString(R.string.blocked_notification_title_template,
+ printJob.getLabel());
+ } else {
+ return mContext.getString(
+ R.string.cancelling_notification_title_template,
+ printJob.getLabel());
+ }
+ }
+
+ default: {
+ if (!printJob.isCancelling()) {
+ return mContext.getString(R.string.printing_notification_title_template,
+ printJob.getLabel());
+ } else {
+ return mContext.getString(
+ R.string.cancelling_notification_title_template,
+ printJob.getLabel());
+ }
+ }
+ }
+ }
+
+ private void removeNotification() {
+ mNotificationManager.cancel(0);
}
private PendingIntent createContentIntent(PrintJobId printJobId) {
Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
- intent.putExtra(EXTRA_PRINT_JOB_ID, printJobId.flattenToString());
- return PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+ if (printJobId != null) {
+ intent.putExtra(EXTRA_PRINT_JOB_ID, printJobId.flattenToString());
+ intent.setData(Uri.fromParts("printjob", printJobId.flattenToString(), null));
+ }
+ return PendingIntent.getActivity(mContext, 0, intent, 0);
}
private PendingIntent createCancelIntent(PrintJobInfo printJob) {
return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
}
+ private static boolean shouldNotifyForState(int state) {
+ switch (state) {
+ case PrintJobInfo.STATE_QUEUED:
+ case PrintJobInfo.STATE_STARTED:
+ case PrintJobInfo.STATE_FAILED:
+ case PrintJobInfo.STATE_COMPLETED:
+ case PrintJobInfo.STATE_CANCELED:
+ case PrintJobInfo.STATE_BLOCKED: {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static int computeNotificationIcon(PrintJobInfo printJob) {
+ switch (printJob.getState()) {
+ case PrintJobInfo.STATE_FAILED:
+ case PrintJobInfo.STATE_BLOCKED: {
+ return com.android.internal.R.drawable.ic_print_error;
+ }
+ default: {
+ if (!printJob.isCancelling()) {
+ return com.android.internal.R.drawable.ic_print;
+ } else {
+ return R.drawable.stat_notify_cancelling;
+ }
+ }
+ }
+ }
+
public static final class NotificationBroadcastReceiver extends BroadcastReceiver {
private static final String LOG_TAG = "NotificationBroadcastReceiver";
Log.i(LOG_TAG, "handleCancelPrintJob() printJobId:" + printJobId);
}
- // Put up a notification that we are trying to cancel.
- NotificationManager notificationManager = (NotificationManager)
- context.getSystemService(Context.NOTIFICATION_SERVICE);
- Notification.Builder builder = new Notification.Builder(context)
- .setSmallIcon(R.drawable.stat_notify_cancelling)
- .setContentTitle(context.getString(
- R.string.cancelling_notification_title_template,
- printJobLabel))
- .setContentText(printerName)
- .setWhen(System.currentTimeMillis())
- .setOngoing(true)
- .setShowWhen(true);
- notificationManager.notify(printJobId.flattenToString(), 0, builder.build());
-
// Call into the print manager service off the main thread since
// the print manager service may end up binding to the print spooler
// service which binding is handled on the main thread.
// done on another thread and until it finishes the spooler has
// to be kept around.
try {
- IPrintManager printManager = IPrintManager.Stub.asInterface(
- ServiceManager.getService(Context.PRINT_SERVICE));
+ IPrintManager printManager = IPrintManager.Stub.asInterface(
+ ServiceManager.getService(Context.PRINT_SERVICE));
printManager.cancelPrintJob(printJobId, PrintManager.APP_ID_ANY,
UserHandle.myUserId());
} catch (RemoteException re) {
// Then update the print jobs's pages as we will not do a write
// and we usually update the pages in the write complete callback.
updatePrintJobPages(mDocument.pages, mRequestedPages);
+ mEditor.updateUi();
if (mEditor.isDone()) {
requestCreatePdfFileOrFinish();
}
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
PrintSpoolerService.this.dump(fd, writer, args);
}
+
+ @Override
+ public void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) {
+ PrintSpoolerService.this.setPrintJobCancelling(printJobId, cancelling);
+ }
};
}
fileForJobMap.remove(printJob.getId());
}
- // Update the notification.
- mNotificationController.onPrintJobStateChanged(printJob);
switch (printJob.getState()) {
case PrintJobInfo.STATE_QUEUED:
case PrintJobInfo.STATE_STARTED:
}
}
+ if (!mPrintJobs.isEmpty()) {
+ // Update the notification.
+ mNotificationController.onUpdateNotifications(mPrintJobs);
+ }
+
// Delete the orphan files.
if (fileForJobMap != null) {
final int orphanFileCount = fileForJobMap.size();
printJob.setState(state);
printJob.setStateReason(error);
- mNotificationController.onPrintJobStateChanged(printJob);
+ printJob.setCancelling(false);
if (DEBUG_PRINT_JOB_LIFECYCLE) {
Slog.i(LOG_TAG, "[STATE CHANGED] " + printJob);
HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
printJob);
mHandlerCaller.executeOrSendMessage(message);
+
+ mNotificationController.onUpdateNotifications(mPrintJobs);
}
}
return false;
}
+ public void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) {
+ synchronized (mLock) {
+ PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
+ if (printJob != null) {
+ printJob.setCancelling(cancelling);
+ if (shouldPersistPrintJob(printJob)) {
+ mPersistanceManager.writeStateLocked();
+ }
+ mNotificationController.onUpdateNotifications(mPrintJobs);
+
+ Message message = mHandlerCaller.obtainMessageO(
+ HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
+ printJob);
+ mHandlerCaller.executeOrSendMessage(message);
+ }
+ }
+ }
+
public void setPrintJobCopiesNoPersistence(PrintJobId printJobId, int copies) {
synchronized (mLock) {
PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
private static final String ATTR_COPIES = "copies";
private static final String ATTR_PRINTER_NAME = "printerName";
private static final String ATTR_STATE_REASON = "stateReason";
+ private static final String ATTR_CANCELLING = "cancelling";
private static final String TAG_MEDIA_SIZE = "mediaSize";
private static final String TAG_RESOLUTION = "resolution";
if (!TextUtils.isEmpty(stateReason)) {
serializer.attribute(null, ATTR_STATE_REASON, stateReason);
}
+ serializer.attribute(null, ATTR_CANCELLING, String.valueOf(
+ printJob.isCancelling()));
PrinterId printerId = printJob.getPrinterId();
if (printerId != null) {
printJob.setPrinterName(printerName);
String stateReason = parser.getAttributeValue(null, ATTR_STATE_REASON);
printJob.setStateReason(stateReason);
+ String cancelling = parser.getAttributeValue(null, ATTR_CANCELLING);
+ printJob.setCancelling(!TextUtils.isEmpty(cancelling)
+ ? Boolean.parseBoolean(cancelling) : false);
parser.next();
}
@Override
+ public void onPause() {
+ if (mAnnounceFilterResult != null) {
+ mAnnounceFilterResult.remove();
+ }
+ super.onPause();
+ }
+
+ @Override
public void onListItemClick(ListView list, View view, int position, long id) {
PrinterInfo printer = (PrinterInfo) list.getAdapter().getItem(position);
Activity activity = getActivity();
}
}
- private void announceSearchResult() {
- if (mAnnounceFilterResult == null) {
- mAnnounceFilterResult = new AnnounceFilterResult();
+ private void announceSearchResultIfNeeded() {
+ if (AccessibilityManager.getInstance(getActivity()).isEnabled()) {
+ if (mAnnounceFilterResult == null) {
+ mAnnounceFilterResult = new AnnounceFilterResult();
+ }
+ mAnnounceFilterResult.post();
}
- mAnnounceFilterResult.post();
}
public static class AddPrinterAlertDialogFragment extends DialogFragment {
resultCountChanged = (oldPrinterCount != mFilteredPrinters.size());
}
if (resultCountChanged) {
- announceSearchResult();
+ announceSearchResultIfNeeded();
}
notifyDataSetChanged();
}
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:orientation="vertical">
- <AnalogClock
- android:id="@+id/analog_clock"
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:layout_gravity="center"
- android:layout_marginBottom="10dp"
- android:dial="@drawable/ic_qs_clock_circle"
- android:hand_hour="@drawable/ic_qs_clock_hour"
- android:hand_minute="@drawable/ic_qs_clock_minute"
- />
- <com.android.systemui.statusbar.policy.DateView
- android:textAppearance="@style/TextAppearance.QuickSettings.TileView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:gravity="center"
- />
-</LinearLayout>
if (DEBUG) Log.d(TAG, "opening search panel");
if (mSearchPanelView != null && mSearchPanelView.isAssistantAvailable()) {
mSearchPanelView.show(true, true);
+ onShowSearchPanel();
}
break;
case MSG_CLOSE_SEARCH_PANEL:
if (DEBUG) Log.d(TAG, "closing search panel");
if (mSearchPanelView != null && mSearchPanelView.isShowing()) {
mSearchPanelView.show(false, true);
+ onHideSearchPanel();
}
break;
}
protected void workAroundBadLayerDrawableOpacity(View v) {
}
+ protected void onHideSearchPanel() {
+ }
+
+ protected void onShowSearchPanel() {
+ }
+
public boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
int minHeight =
mContext.getResources().getDimensionPixelSize(R.dimen.notification_min_height);
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
-import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
// used to disable the camera icon in navbar when disabled by DPM
private boolean mCameraDisabledByDpm;
+ // simplified click handler to be used when device is in accessibility mode
+ private final OnClickListener mAccessibilityClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.camera_button) {
+ KeyguardTouchDelegate.getInstance(getContext()).launchCamera();
+ } else if (v.getId() == R.id.search_light) {
+ KeyguardTouchDelegate.getInstance(getContext()).showAssistant();
+ }
+ }
+ };
+
private final OnTouchListener mCameraTouchListener = new OnTouchListener() {
@Override
public boolean onTouch(View cameraButtonView, MotionEvent event) {
- View searchLight = getSearchLight();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// disable search gesture while interacting with camera
mDelegateHelper.setDisabled(true);
- cameraButtonView.animate().alpha(0.0f).setDuration(CAMERA_BUTTON_FADE_DURATION);
- if (searchLight != null) {
- searchLight.animate().alpha(0.0f).setDuration(CAMERA_BUTTON_FADE_DURATION);
- }
+ transitionCameraAndSearchButtonAlpha(0.0f);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mDelegateHelper.setDisabled(false);
- cameraButtonView.animate().alpha(1.0f).setDuration(CAMERA_BUTTON_FADE_DURATION);
- if (searchLight != null) {
- searchLight.animate().alpha(1.0f).setDuration(CAMERA_BUTTON_FADE_DURATION);
- }
+ transitionCameraAndSearchButtonAlpha(1.0f);
break;
}
return KeyguardTouchDelegate.getInstance(getContext()).dispatch(event);
watchForDevicePolicyChanges();
}
+ protected void transitionCameraAndSearchButtonAlpha(float alpha) {
+ View cameraButtonView = getCameraButton();
+ if (cameraButtonView != null) {
+ cameraButtonView.animate().alpha(alpha).setDuration(CAMERA_BUTTON_FADE_DURATION);
+ }
+ View searchLight = getSearchLight();
+ if (searchLight != null) {
+ searchLight.animate().alpha(alpha).setDuration(CAMERA_BUTTON_FADE_DURATION);
+ }
+ }
+
private void watchForDevicePolicyChanges() {
final IntentFilter filter = new IntentFilter();
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
mCurrentView = mRotatedViews[Surface.ROTATION_0];
+ watchForAccessibilityChanges();
+ }
- final AccessibilityManager accessibilityManager =
+ private void watchForAccessibilityChanges() {
+ final AccessibilityManager am =
(AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
- if (accessibilityManager.isEnabled() && accessibilityManager.isTouchExplorationEnabled()) {
- // In accessibility mode, we add a simple click handler since swipe is tough to
- // trigger near screen edges.
- View camera = getCameraButton();
- View searchLight = getSearchLight();
- if (camera != null || searchLight != null) {
- OnClickListener listener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- launchForAccessibilityClick(v);
- }
- };
- if (camera != null) {
- camera.setOnClickListener(listener);
- }
- if (searchLight != null) {
- searchLight.setOnClickListener(listener);
- }
+
+ // Set the initial state
+ enableAccessibility(am.isTouchExplorationEnabled());
+
+ // Watch for changes
+ am.addTouchExplorationStateChangeListener(new TouchExplorationStateChangeListener() {
+ @Override
+ public void onTouchExplorationStateChanged(boolean enabled) {
+ enableAccessibility(enabled);
}
- } else {
- // Add a touch handler for camera icon for all view orientations.
- for (int i = 0; i < mRotatedViews.length; i++) {
- View cameraButton = mRotatedViews[i].findViewById(R.id.camera_button);
- if (cameraButton != null) {
- cameraButton.setOnTouchListener(mCameraTouchListener);
- }
+ });
+ }
+
+ private void enableAccessibility(boolean touchEnabled) {
+ Log.v(TAG, "touchEnabled:" + touchEnabled);
+
+ // Add a touch handler or accessibility click listener for camera and search buttons
+ // for all view orientations.
+ final OnClickListener onClickListener = touchEnabled ? mAccessibilityClickListener : null;
+ final OnTouchListener onTouchListener = touchEnabled ? null : mCameraTouchListener;
+ boolean hasCamera = false;
+ for (int i = 0; i < mRotatedViews.length; i++) {
+ final View cameraButton = mRotatedViews[i].findViewById(R.id.camera_button);
+ final View searchLight = mRotatedViews[i].findViewById(R.id.search_light);
+ if (cameraButton != null) {
+ hasCamera = true;
+ cameraButton.setOnTouchListener(onTouchListener);
+ cameraButton.setOnClickListener(onClickListener);
+ }
+ if (searchLight != null) {
+ searchLight.setOnClickListener(onClickListener);
}
}
- }
-
- protected void launchForAccessibilityClick(View v) {
- if (v == getCameraButton()) {
- KeyguardTouchDelegate.getInstance(getContext()).launchCamera();
- } else if (v == getSearchLight()) {
- KeyguardTouchDelegate.getInstance(getContext()).showAssistant();
+ if (hasCamera) {
+ // Warm up KeyguardTouchDelegate so it's ready by the time the camera button is touched.
+ // This will connect to KeyguardService so that touch events are processed.
+ KeyguardTouchDelegate.getInstance(mContext);
}
}
}
@Override
+ protected void onShowSearchPanel() {
+ if (mNavigationBarView != null) {
+ mNavigationBarView.transitionCameraAndSearchButtonAlpha(0.0f);
+ }
+ }
+
+ @Override
+ protected void onHideSearchPanel() {
+ if (mNavigationBarView != null) {
+ mNavigationBarView.transitionCameraAndSearchButtonAlpha(1.0f);
+ }
+ }
+
+ @Override
protected View getStatusBarView() {
return mStatusBarView;
}
}
private void checkBarMode(int mode, int windowState, BarTransitions transitions) {
- final boolean imeVisible = (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0;
- final int finalMode = imeVisible ? MODE_OPAQUE : mode;
final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN;
- transitions.transitionTo(finalMode, anim);
+ transitions.transitionTo(mode, anim);
}
private final Runnable mCheckBarModes = new Runnable() {
parent.addView(brightnessTile);
mDynamicSpannedTiles.add(brightnessTile);
- // Time tile
- /*
- QuickSettingsTileView timeTile = (QuickSettingsTileView)
- inflater.inflate(R.layout.quick_settings_tile, parent, false);
- timeTile.setContent(R.layout.quick_settings_tile_time, inflater);
- timeTile.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // Quick. Clock. Quick. Clock. Quick. Clock.
- startSettingsActivity(Intent.ACTION_QUICK_CLOCK);
- }
- });
- mModel.addTimeTile(timeTile, new QuickSettingsModel.RefreshCallback() {
- @Override
- public void refreshView(QuickSettingsTileView view, State alarmState) {}
- });
- parent.addView(timeTile);
- mDynamicSpannedTiles.add(timeTile);
- */
-
// Settings tile
final QuickSettingsBasicTile settingsTile = new QuickSettingsBasicTile(mContext);
settingsTile.setImageResource(R.drawable.ic_qs_settings);
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
refreshBluetoothTile();
refreshBrightnessTile();
refreshRotationLockTile();
+ refreshRssiTile();
}
// Settings
}
}
+ void refreshRssiTile() {
+ if (mRSSITile != null) {
+ // We reinflate the original view due to potential styling changes that may have
+ // taken place due to a configuration change.
+ mRSSITile.reinflateContent(LayoutInflater.from(mContext));
+ }
+ }
+
// Bluetooth
void addBluetoothTile(QuickSettingsTileView view, RefreshCallback cb) {
mBluetoothTile = view;
import android.content.Context;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
*
*/
class QuickSettingsTileView extends FrameLayout {
+ private static final String TAG = "QuickSettingsTileView";
+ private int mContentLayoutId;
private int mColSpan;
private int mRowSpan;
public QuickSettingsTileView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mContentLayoutId = -1;
mColSpan = 1;
mRowSpan = 1;
}
}
void setContent(int layoutId, LayoutInflater inflater) {
+ mContentLayoutId = layoutId;
inflater.inflate(layoutId, this);
}
+ void reinflateContent(LayoutInflater inflater) {
+ if (mContentLayoutId != -1) {
+ removeAllViews();
+ setContent(mContentLayoutId, inflater);
+ } else {
+ Log.e(TAG, "Not reinflating content: No layoutId set");
+ }
+ }
+
@Override
public void setVisibility(int vis) {
if (QuickSettings.DEBUG_GONE_TILES) {
|| Intent.ACTION_TIME_CHANGED.equals(action)
|| Intent.ACTION_TIMEZONE_CHANGED.equals(action)
|| Intent.ACTION_LOCALE_CHANGED.equals(action)) {
- if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
+ if (Intent.ACTION_LOCALE_CHANGED.equals(action)
+ || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
// need to get a fresh date format
mDateFormat = null;
}
pf.left = df.left = of.left = cf.left = vf.left = mDockLeft;
pf.top = df.top = of.top = cf.top = vf.top = mDockTop;
pf.right = df.right = of.right = cf.right = vf.right = mDockRight;
- // IM dock windows always go above the nav bar.
- pf.bottom = df.bottom = of.bottom = cf.bottom = vf.bottom = mStableBottom;
+ // IM dock windows layout below the nav bar...
+ pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ // ...with content insets above the nav bar
+ cf.bottom = vf.bottom = mStableBottom;
// IM dock windows always go to the bottom of the screen.
attrs.gravity = Gravity.BOTTOM;
mDockLayer = win.getSurfaceLayer();
})) {
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);
}
android.app.ActivityThread$ProviderKey
android.app.ActivityThread$ProviderRefCount
android.app.ActivityThread$ReceiverData
-android.app.ActivityThread$ResourcesKey
android.app.ActivityThread$ResultData
android.app.ActivityThread$ServiceArgsData
android.app.ActivityThread$StopInfo
android.preference.CheckBoxPreference
android.preference.GenericInflater
android.preference.GenericInflater$Parent
-android.preference.OnDependencyChangeListener
android.preference.Preference
android.preference.Preference$OnPreferenceChangeInternalListener
android.preference.Preference$OnPreferenceChangeListener
android.view.Choreographer$FrameDisplayEventReceiver
android.view.Choreographer$FrameHandler
android.view.CollapsibleActionView
-android.view.CompatibilityInfoHolder
android.view.ContextMenu
android.view.ContextMenu$ContextMenuInfo
android.view.ContextThemeWrapper
android.widget.EditText
android.widget.Editor
android.widget.Editor$Blink
-android.widget.Editor$EasyEditSpanController
android.widget.Editor$InputContentType
android.widget.Editor$InputMethodState
-android.widget.Editor$UserDictionaryListener
android.widget.ExpandableListView
android.widget.Filter
android.widget.Filter$FilterListener
java.lang.UnsatisfiedLinkError
java.lang.UnsupportedOperationException
java.lang.VMClassLoader
-java.lang.VMThread
java.lang.VerifyError
java.lang.VirtualMachineError
java.lang.Void
java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock
java.util.jar.Attributes
java.util.jar.Attributes$Name
-java.util.jar.InitManifest
java.util.jar.JarEntry
java.util.jar.JarFile
java.util.jar.JarFile$1JarFileEnumerator
case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK:
{
IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
- mStateChangeCallbacks.register(callback);
+ if (callback != null) {
+ mStateChangeCallbacks.register(callback);
+ }
break;
}
case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK:
{
IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
- mStateChangeCallbacks.unregister(callback);
+ if (callback != null) {
+ mStateChangeCallbacks.unregister(callback);
+ }
break;
}
case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
*/
private static final int EVENT_SAMPLE_INTERVAL_ELAPSED = 15;
+ /**
+ * PAC manager has received new port.
+ */
+ private static final int EVENT_PROXY_HAS_CHANGED = 16;
+
/** Handler used for internal events. */
private InternalHandler mHandler;
/** Handler used for incoming {@link NetworkStateTracker} events. */
},
new IntentFilter(filter));
- mPacManager = new PacManager(mContext);
+ mPacManager = new PacManager(mContext, mHandler, EVENT_PROXY_HAS_CHANGED);
filter = new IntentFilter();
filter.addAction(CONNECTED_TO_PROVISIONING_NETWORK_ACTION);
handleNetworkSamplingTimeout();
break;
}
+ case EVENT_PROXY_HAS_CHANGED: {
+ handleApplyDefaultProxy((ProxyProperties)msg.obj);
+ break;
+ }
}
}
}
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();
}
if (packageChanged) {
// We cancel notifications for packages which have just been disabled
- final int enabled = mContext.getPackageManager()
- .getApplicationEnabledSetting(pkgName);
- if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
- || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
- cancelNotifications = false;
+ try {
+ final int enabled = mContext.getPackageManager()
+ .getApplicationEnabledSetting(pkgName);
+ if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+ cancelNotifications = false;
+ }
+ } catch (IllegalArgumentException e) {
+ // Package doesn't exist; probably racing with uninstall.
+ // cancelNotifications is already true, so nothing to do here.
+ if (DBG) {
+ Slog.i(TAG, "Exception trying to look up app enabled setting", e);
+ }
}
}
pkgList = new String[]{pkgName};
+ "there is at least one pointer down!");
}
case MotionEvent.ACTION_UP: {
+ mAms.onTouchInteractionEnd();
// Announce the end of a the touch interaction.
sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
- mAms.onTouchInteractionEnd();
mLongPressingPointerId = -1;
mLongPressingPointerDeltaX = 0;
mLongPressingPointerDeltaY = 0;
AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
if (accessibilityManager.isEnabled()) {
AccessibilityEvent event = AccessibilityEvent.obtain(type);
+ event.setWindowId(mAms.getActiveWindowId());
accessibilityManager.sendAccessibilityEvent(event);
switch (type) {
case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START: {
return mUserManager;
}
- private UserAccounts initUser(int userId) {
- synchronized (mUsers) {
- UserAccounts accounts = mUsers.get(userId);
- if (accounts == null) {
- accounts = new UserAccounts(mContext, userId);
- mUsers.append(userId, accounts);
- purgeOldGrants(accounts);
- validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
- }
- return accounts;
+ /* Caller should lock mUsers */
+ private UserAccounts initUserLocked(int userId) {
+ UserAccounts accounts = mUsers.get(userId);
+ if (accounts == null) {
+ accounts = new UserAccounts(mContext, userId);
+ mUsers.append(userId, accounts);
+ purgeOldGrants(accounts);
+ validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
}
+ return accounts;
}
private void purgeOldGrantsAll() {
synchronized (mUsers) {
UserAccounts accounts = mUsers.get(userId);
if (accounts == null) {
- accounts = initUser(userId);
+ accounts = initUserLocked(userId);
mUsers.append(userId, accounts);
}
return accounts;
private AccountAndUser[] getAccounts(int[] userIds) {
final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
- synchronized (mUsers) {
- for (int userId : userIds) {
- UserAccounts userAccounts = getUserAccounts(userId);
- if (userAccounts == null) continue;
- synchronized (userAccounts.cacheLock) {
- Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
- Binder.getCallingUid(), null);
- for (int a = 0; a < accounts.length; a++) {
- runningAccounts.add(new AccountAndUser(accounts[a], userId));
- }
+ for (int userId : userIds) {
+ UserAccounts userAccounts = getUserAccounts(userId);
+ if (userAccounts == null) continue;
+ synchronized (userAccounts.cacheLock) {
+ Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
+ Binder.getCallingUid(), null);
+ for (int a = 0; a < accounts.length; a++) {
+ runningAccounts.add(new AccountAndUser(accounts[a], userId));
}
}
}
|| callingUid == Process.myUid()) {
return unfiltered;
}
- if (mUserManager.getUserInfo(userAccounts.userId).isRestricted()) {
+ UserInfo user = mUserManager.getUserInfo(userAccounts.userId);
+ if (user != null && user.isRestricted()) {
String[] packages = mPackageManager.getPackagesForUid(callingUid);
// If any of the packages is a white listed package, return the full set,
// otherwise return non-shared accounts only.
}
@Override
- public ParceledListSlice<android.content.UriPermission> getPersistedUriPermissions() {
+ public ParceledListSlice<android.content.UriPermission> getPersistedUriPermissions(
+ String packageName, boolean incoming) {
enforceNotIsolatedCaller("getPersistedUriPermissions");
+ Preconditions.checkNotNull(packageName, "packageName");
+ final int callingUid = Binder.getCallingUid();
+ final IPackageManager pm = AppGlobals.getPackageManager();
+ try {
+ final int packageUid = pm.getPackageUid(packageName, UserHandle.getUserId(callingUid));
+ if (packageUid != callingUid) {
+ throw new SecurityException(
+ "Package " + packageName + " does not belong to calling UID " + callingUid);
+ }
+ } catch (RemoteException e) {
+ throw new SecurityException("Failed to verify package name ownership");
+ }
+
+ final ArrayList<android.content.UriPermission> result = Lists.newArrayList();
synchronized (this) {
- final int callingUid = Binder.getCallingUid();
- final ArrayList<android.content.UriPermission> result = Lists.newArrayList();
- final ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
- if (perms == null) {
- Slog.w(TAG, "No permission grants found for UID " + callingUid);
+ if (incoming) {
+ final ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
+ if (perms == null) {
+ Slog.w(TAG, "No permission grants found for " + packageName);
+ } else {
+ final int size = perms.size();
+ for (int i = 0; i < size; i++) {
+ final UriPermission perm = perms.valueAt(i);
+ if (packageName.equals(perm.targetPkg) && perm.persistedModeFlags != 0) {
+ result.add(perm.buildPersistedPublicApiObject());
+ }
+ }
+ }
} else {
- final int size = perms.size();
+ final int size = mGrantedUriPermissions.size();
for (int i = 0; i < size; i++) {
- final UriPermission perm = perms.valueAt(i);
- if (perm.persistedModeFlags != 0) {
- result.add(perm.buildPersistedPublicApiObject());
+ final ArrayMap<Uri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
+ final int permsSize = perms.size();
+ for (int j = 0; j < permsSize; j++) {
+ final UriPermission perm = perms.valueAt(j);
+ if (packageName.equals(perm.sourcePkg) && perm.persistedModeFlags != 0) {
+ result.add(perm.buildPersistedPublicApiObject());
+ }
}
}
}
- return new ParceledListSlice<android.content.UriPermission>(result);
}
+ return new ParceledListSlice<android.content.UriPermission>(result);
}
@Override
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 && proc.thread != 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);
}
}
+ boolean mayBeTop = false;
+
for (int is = app.services.size()-1;
is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
schedGroup = Process.THREAD_GROUP_DEFAULT;
}
- if (clientProcState <=
- ActivityManager.PROCESS_STATE_PERSISTENT_UI &&
- clientProcState >=
- ActivityManager.PROCESS_STATE_PERSISTENT) {
- // Persistent processes don't allow us to become top.
- // However the top process DOES allow us to become top,
- // because in that case we are running because the current
- // top process wants us, so we should be counted as part
- // of the top set and not just running for some random
- // unknown reason in the background.
- clientProcState =
- ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {
+ if (clientProcState == ActivityManager.PROCESS_STATE_TOP) {
+ // Special handling of clients who are in the top state.
+ // We *may* want to consider this process to be in the
+ // top state as well, but only if there is not another
+ // reason for it to be running. Being on the top is a
+ // special state, meaning you are specifically running
+ // for the current top app. If the process is already
+ // running in the background for some other reason, it
+ // is more important to continue considering it to be
+ // in the background state.
+ mayBeTop = true;
+ clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ } else {
+ // Special handling for above-top states (persistent
+ // processes). These should not bring the current process
+ // into the top state, since they are not on top. Instead
+ // give them the best state after that.
+ clientProcState =
+ ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ }
}
} else {
if (clientProcState <
app.adjSourceOom = clientAdj;
app.adjTarget = cpr.name;
}
- if (clientProcState <=
- ActivityManager.PROCESS_STATE_PERSISTENT_UI &&
- clientProcState >=
- ActivityManager.PROCESS_STATE_PERSISTENT) {
- // Persistent processes don't allow us to become top.
- // However the top process DOES allow us to become top,
- // because in that case we are running because the current
- // top process wants us, so we should be counted as part
- // of the top set and not just running for some random
- // unknown reason in the background.
- clientProcState =
- ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {
+ if (clientProcState == ActivityManager.PROCESS_STATE_TOP) {
+ // Special handling of clients who are in the top state.
+ // We *may* want to consider this process to be in the
+ // top state as well, but only if there is not another
+ // reason for it to be running. Being on the top is a
+ // special state, meaning you are specifically running
+ // for the current top app. If the process is already
+ // running in the background for some other reason, it
+ // is more important to continue considering it to be
+ // in the background state.
+ mayBeTop = true;
+ clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ } else {
+ // Special handling for above-top states (persistent
+ // processes). These should not bring the current process
+ // into the top state, since they are not on top. Instead
+ // give them the best state after that.
+ clientProcState =
+ ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ }
}
if (procState > clientProcState) {
procState = clientProcState;
}
}
+ if (mayBeTop && procState > ActivityManager.PROCESS_STATE_TOP) {
+ // A client of one of our services or providers is in the top state. We
+ // *may* want to be in the top state, but not if we are already running in
+ // the background for some other reason. For the decision here, we are going
+ // to pick out a few specific states that we want to remain in when a client
+ // is top (states that tend to be longer-term) and otherwise allow it to go
+ // to the top state.
+ switch (procState) {
+ case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
+ case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
+ case ActivityManager.PROCESS_STATE_SERVICE:
+ // These all are longer-term states, so pull them up to the top
+ // of the background states, but not all the way to the top state.
+ procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ break;
+ default:
+ // Otherwise, top is a better choice, so take it.
+ procState = ActivityManager.PROCESS_STATE_TOP;
+ break;
+ }
+ }
+
if (adj == ProcessList.SERVICE_ADJ) {
if (doingAll) {
app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3);
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;
import android.net.Uri;
import android.os.UserHandle;
import android.util.Log;
-import android.util.Slog;
import com.android.internal.util.Preconditions;
import com.google.android.collect.Sets;
import android.net.Proxy;
import android.net.ProxyProperties;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
private boolean mHasSentBroadcast;
private boolean mHasDownloaded;
+ private Handler mConnectivityHandler;
+ private int mProxyMessage;
+
/**
* Used for locking when setting mProxyService and all references to mPacUrl or mCurrentPac.
*/
}
}
- public PacManager(Context context) {
+ public PacManager(Context context, Handler handler, int proxyMessage) {
mContext = context;
mLastPort = -1;
context, 0, new Intent(ACTION_PAC_REFRESH), 0);
context.registerReceiver(new PacRefreshIntentReceiver(),
new IntentFilter(ACTION_PAC_REFRESH));
+ mConnectivityHandler = handler;
+ mProxyMessage = proxyMessage;
}
private AlarmManager getAlarmManager() {
*/
public synchronized boolean setCurrentProxyScriptUrl(ProxyProperties proxy) {
if (!TextUtils.isEmpty(proxy.getPacFileUrl())) {
+ if (proxy.getPacFileUrl().equals(mPacUrl)) {
+ // Allow to send broadcast, nothing to do.
+ return false;
+ }
synchronized (mProxyLock) {
mPacUrl = proxy.getPacFileUrl();
}
}
private void sendPacBroadcast(ProxyProperties proxy) {
- Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
- Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy);
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
}
private synchronized void sendProxyIfNeeded() {
return false;
}
+ public final void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ mCanUnbind = false;
+ }
+ try {
+ getRemoteInstanceLazy().setPrintJobCancelling(printJobId,
+ cancelling);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error setting print job cancelling.", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error setting print job cancelling.", te);
+ } finally {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
+ + "] setPrintJobCancelling()");
+ }
+ synchronized (mLock) {
+ mCanUnbind = true;
+ mLock.notifyAll();
+ }
+ }
+ }
+
public final void removeObsoletePrintJobs() {
throwIfCalledOnMainThread();
synchronized (mLock) {
if (printJobInfo == null) {
return;
}
+
+ // Take a note that we are trying to cancel the job.
+ mSpooler.setPrintJobCancelling(printJobId, true);
+
if (printJobInfo.getState() != PrintJobInfo.STATE_FAILED) {
ComponentName printServiceName = printJobInfo.getPrinterId().getServiceName();
RemotePrintService printService = null;
throw new IllegalArgumentException("addAppToken: invalid stackId=" + stackId);
}
Task task = new Task(atoken, stack, userId);
+ mTaskIdToTask.put(taskId, task);
stack.addTask(task, true);
stack.getDisplayContent().moveStack(stack, true);
- mTaskIdToTask.put(taskId, task);
return task;
}
return index;
}
- private void moveHomeTasksLocked(boolean toTop) {
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
- if (toTop ^ displayContent.homeOnTop()) {
- final ArrayList<Task> tasks = displayContent.getHomeStack().getTasks();
- final int numTasks = tasks.size();
- for (int i = 0; i < numTasks; ++i) {
- if (toTop) {
- // Keep pulling the bottom task off and moving it to the top.
- moveTaskToTop(tasks.get(0).taskId);
- } else {
- // Keep pulling the top task off and moving it to the bottom.
- moveTaskToBottom(tasks.get(numTasks - 1).taskId);
- }
- }
- }
- }
-
void moveStackWindowsLocked(TaskStack stack) {
DisplayContent displayContent = stack.getDisplayContent();
final TaskStack stack = task.mStack;
final DisplayContent displayContent = task.getDisplayContent();
final boolean isHomeStackTask = stack.isHomeStack();
- final boolean homeIsOnTop = displayContent.homeOnTop();
- if (!isHomeStackTask && homeIsOnTop) {
- // First move move the home tasks all to the bottom to rearrange the windows.
- moveHomeTasksLocked(false);
- // Now move the stack itself.
- displayContent.moveHomeStackBox(false);
- } else if (isHomeStackTask && !homeIsOnTop) {
- // Move the stack to the top.
- displayContent.moveHomeStackBox(true);
+ if (isHomeStackTask != displayContent.homeOnTop()) {
+ // First move the stack itself.
+ displayContent.moveHomeStackBox(isHomeStackTask);
}
stack.moveTaskToTop(task);
displayContent.moveStack(stack, true);
//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;
}
/**
* Requires system permission.
*/
void setPremiumSmsPermission(String packageName, int permission);
+
+ /**
+ * SMS over IMS is supported if IMS is registered and SMS is supported
+ * on IMS.
+ *
+ * @return true if SMS over IMS is supported, false otherwise
+ *
+ * @see #getImsSmsFormat()
+ */
+ boolean isImsSmsSupported();
+
+ /**
+ * Gets SMS format supported on IMS. SMS over IMS format is
+ * either 3GPP or 3GPP2.
+ *
+ * @return android.telephony.SmsMessage.FORMAT_3GPP,
+ * android.telephony.SmsMessage.FORMAT_3GPP2
+ * or android.telephony.SmsMessage.FORMAT_UNKNOWN
+ *
+ * @see #isImsSmsSupported()
+ */
+ String getImsSmsFormat();
}
int RIL_REQUEST_GET_CELL_INFO_LIST = 109;
int RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE = 110;
int RIL_REQUEST_SET_INITIAL_ATTACH_APN = 111;
+ int RIL_REQUEST_IMS_REGISTRATION_STATE = 112;
+ int RIL_REQUEST_IMS_SEND_SMS = 113;
int RIL_UNSOL_RESPONSE_BASE = 1000;
int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000;
int RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED = 1001;
int RIL_UNSOL_RIL_CONNECTED = 1034;
int RIL_UNSOL_VOICE_RADIO_TECH_CHANGED = 1035;
int RIL_UNSOL_CELL_INFO_LIST = 1036;
+ int RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED = 1037;
}
}
/**
+ * Indicates unknown format SMS message.
+ * @hide pending API council approval
+ */
+ public static final String FORMAT_UNKNOWN = "unknown";
+
+ /**
* Indicates a 3GPP format SMS message.
* @hide pending API council approval
*/
if (mOperationalMode != CONNECT_MODE) {
mWifiNative.disconnect();
mWifiConfigStore.disableAllNetworks();
- setWifiState(WIFI_STATE_DISABLED);
+ if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
+ setWifiState(WIFI_STATE_DISABLED);
+ }
transitionTo(mScanModeState);
} else {
/* Driver stop may have disabled networks, enable right after start */
@Override
public void exit() {
- // if we're leaving before this is done, cleanup
- if (mDhcpActive) {
- handlePostDhcpSetup();
- }
+ handleNetworkDisconnect();
}
@Override