diff --git a/Android.mk b/Android.mk
index 343b124de2b6ce430a1143030725cde5bee831c7..ee4d777132f9e1015bc8c1464efb61f47c0e59b6 100644
--- a/Android.mk
+++ b/Android.mk
@@ -173,6 +173,7 @@ LOCAL_SRC_FILES += \
core/java/android/print/ILayoutResultCallback.aidl \
core/java/android/print/IPrinterDiscoveryObserver.aidl \
core/java/android/print/IPrintDocumentAdapter.aidl \
+ core/java/android/print/IPrintDocumentAdapterObserver.aidl \
core/java/android/print/IPrintJobStateChangeListener.aidl \
core/java/android/print/IPrintManager.aidl \
core/java/android/print/IPrintSpooler.aidl \
@@ -260,10 +261,14 @@ LOCAL_SRC_FILES += \
media/java/android/media/IAudioService.aidl \
media/java/android/media/IAudioFocusDispatcher.aidl \
media/java/android/media/IAudioRoutesObserver.aidl \
+ media/java/android/media/IMediaRouterClient.aidl \
+ media/java/android/media/IMediaRouterService.aidl \
media/java/android/media/IMediaScannerListener.aidl \
media/java/android/media/IMediaScannerService.aidl \
media/java/android/media/IRemoteControlClient.aidl \
media/java/android/media/IRemoteControlDisplay.aidl \
+ media/java/android/media/IRemoteDisplayCallback.aidl \
+ media/java/android/media/IRemoteDisplayProvider.aidl \
media/java/android/media/IRemoteVolumeObserver.aidl \
media/java/android/media/IRingtonePlayer.aidl \
telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
@@ -553,31 +558,89 @@ framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= \
framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES := \
frameworks/base/docs/knowntags.txt
-sample_dir := development/samples
+sample_dir := development/samples/browseable
new_sample_dir := developers/samples/android
# Whitelist of valid groups, used for default TOC grouping. Each sample must
# belong to one (and only one) group. Assign samples to groups by setting
# a sample.group var to one of these groups in the sample's _index.jd.
-sample_groups := -samplegroup Input \
- -samplegroup Sensors \
- -samplegroup Connectivity
+sample_groups := -samplegroup Background \
+ -samplegroup Connectivity \
+ -samplegroup Content \
+ -samplegroup Input \
+ -samplegroup Media \
+ -samplegroup Security \
+ -samplegroup Testing \
+ -samplegroup UI \
+ -samplegroup Views
# the list here should match the list of samples included in the sdk samples package
# (see development/build/sdk.atree)
# remove htmlified samples for now -- samples are still available through the SDK
web_docs_sample_code_flags := \
-hdf android.hasSamples 1 \
- -samplecode $(new_sample_dir)/input/gestures/BasicGestureDetect/BasicGestureDetect \
- samples/BasicGestureDetect/ "Basic Gestures" \
- -samplecode $(sample_dir)/AccelerometerPlay \
- samples/AccelerometerPlay "Accelerometer Play" \
- -samplecode $(sample_dir)/ActionBarCompat \
- samples/ActionBarCompat "Action Bar Compatibility" \
- -samplecode $(sample_dir)/BluetoothHDP \
- samples/BluetoothHDP "Bluetooth HDP Demo" \
- -samplecode $(sample_dir)/BluetoothLeGatt \
- samples/BluetoothLeGatt "Bluetooth HDP Demo"
+ -samplecode $(sample_dir)/BasicAccessibility \
+ samples/BasicAccessibility "" \
+ -samplecode $(sample_dir)/HorizontalPaging \
+ samples/HorizontalPaging "" \
+ -samplecode $(sample_dir)/ShareActionProvider \
+ samples/ShareActionProvider "" \
+ -samplecode $(sample_dir)/Styled \
+ samples/Styled "" \
+ -samplecode $(sample_dir)/BasicAndroidKeyStore \
+ samples/BasicAndroidKeyStore "" \
+ -samplecode $(sample_dir)/Basic \
+ samples/Basic "" \
+ -samplecode $(sample_dir)/ImmersiveMode \
+ samples/ImmersiveMode "" \
+ -samplecode $(sample_dir)/repeatingAlarm \
+ samples/repeatingAlarm "" \
+ -samplecode $(sample_dir)/TextLinkify \
+ samples/TextLinkify "" \
+ -samplecode $(sample_dir)/BasicMediaRouter \
+ samples/BasicMediaRouter "" \
+ -samplecode $(sample_dir)/BasicMultitouch \
+ samples/BasicMultitouch "" \
+ -samplecode $(sample_dir)/TextSwitcher \
+ samples/TextSwitcher "" \
+ -samplecode $(sample_dir)/ActivityInstrumentation \
+ samples/ActivityInstrumentation "" \
+ -samplecode $(sample_dir)/BorderlessButtons \
+ samples/BorderlessButtons "" \
+ -samplecode $(sample_dir)/BasicNotifications \
+ samples/BasicNotifications "" \
+ -samplecode $(sample_dir)/AdvancedImmersiveMode \
+ samples/AdvancedImmersiveMode "" \
+ -samplecode $(sample_dir)/BluetoothLeGatt \
+ samples/BluetoothLeGatt "" \
+ -samplecode $(sample_dir)/NetworkConnect \
+ samples/NetworkConnect "" \
+ -samplecode $(sample_dir)/BasicNetworking \
+ samples/BasicNetworking "" \
+ -samplecode $(sample_dir)/BasicMediaDecoder \
+ samples/BasicMediaDecoder "" \
+ -samplecode $(sample_dir)/BasicImmersiveMode \
+ samples/BasicImmersiveMode "" \
+ -samplecode $(sample_dir)/CustomChoiceList \
+ samples/CustomChoiceList "" \
+ -samplecode $(sample_dir)/BasicContactables \
+ samples/BasicContactables "" \
+ -samplecode $(sample_dir)/BasicGestureDetect \
+ samples/BasicGestureDetect "" \
+ -samplecode $(sample_dir)/DoneBar \
+ samples/DoneBar "" \
+ -samplecode $(sample_dir)/ListPopupMenu \
+ samples/ListPopupMenu "" \
+ -samplecode $(sample_dir)/AppRestrictions \
+ samples/AppRestrictions "" \
+ -samplecode $(sample_dir)/CustomNotifications \
+ samples/CustomNotifications "" \
+ -samplecode $(sample_dir)/BasicSyncAdapter \
+ samples/BasicSyncAdapter "" \
+ -samplecode $(sample_dir)/StorageClient \
+ samples/StorageClient ""
+# -samplecode $(sample_dir)/StorageProvider \
+# samples/StorageProvider ""
# -samplecode $(sample_dir)/AndroidBeamDemo \
# samples/AndroidBeamDemo "Android Beam Demo" \
# -samplecode $(sample_dir)/ApiDemos \
@@ -796,9 +859,9 @@ LOCAL_MODULE := online-sdk
LOCAL_DROIDDOC_OPTIONS:= \
$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-toroot / \
- -hdf android.whichdoc online
-# $(sample_groups) \
-# $(web_docs_sample_code_flags)
+ -hdf android.whichdoc online \
+ $(sample_groups) \
+ $(web_docs_sample_code_flags)
LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index c18f54259678e8ac4893e20c7083dd78d0ef15d4..0344d261fd35f75591c022acbd71ef17d67f7f0c 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -107,7 +107,7 @@ public class Am extends BaseCommand {
" am switch-user
+ * Refer to {@link MediaRouteButton} for a description of the button that will + * appear in the action bar menu. Note that instead of disabling the button + * when no routes are available, the action provider will instead make the + * menu item invisible. In this way, the button will only be visible when it + * is possible for the user to discover and select a matching route. + *
+ */ public class MediaRouteActionProvider extends ActionProvider { private static final String TAG = "MediaRouteActionProvider"; - private Context mContext; - private MediaRouter mRouter; - private MenuItem mMenuItem; - private MediaRouteButton mView; + private final Context mContext; + private final MediaRouter mRouter; + private final MediaRouterCallback mCallback; + private int mRouteTypes; + private MediaRouteButton mButton; private View.OnClickListener mExtendedSettingsListener; - private RouterCallback mCallback; public MediaRouteActionProvider(Context context) { super(context); + mContext = context; mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE); - mCallback = new RouterCallback(this); + mCallback = new MediaRouterCallback(this); // Start with live audio by default. // TODO Update this when new route types are added; segment by API level @@ -53,80 +66,74 @@ public class MediaRouteActionProvider extends ActionProvider { setRouteTypes(MediaRouter.ROUTE_TYPE_LIVE_AUDIO); } + /** + * Sets the types of routes that will be shown in the media route chooser dialog + * launched by this button. + * + * @param types The route types to match. + */ public void setRouteTypes(int types) { - if (mRouteTypes == types) return; - if (mRouteTypes != 0) { - mRouter.removeCallback(mCallback); - } - mRouteTypes = types; - if (types != 0) { - mRouter.addCallback(types, mCallback); + if (mRouteTypes != types) { + // FIXME: We currently have no way of knowing whether the action provider + // is still needed by the UI. Unfortunately this means the action provider + // may leak callbacks until garbage collection occurs. This may result in + // media route providers doing more work than necessary in the short term + // while trying to discover routes that are no longer of interest to the + // application. To solve this problem, the action provider will need some + // indication from the framework that it is being destroyed. + if (mRouteTypes != 0) { + mRouter.removeCallback(mCallback); + } + mRouteTypes = types; + if (types != 0) { + mRouter.addCallback(types, mCallback, + MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY); + } + refreshRoute(); + + if (mButton != null) { + mButton.setRouteTypes(mRouteTypes); + } } - if (mView != null) { - mView.setRouteTypes(mRouteTypes); + } + + public void setExtendedSettingsClickListener(View.OnClickListener listener) { + mExtendedSettingsListener = listener; + if (mButton != null) { + mButton.setExtendedSettingsClickListener(listener); } } @Override + @SuppressWarnings("deprecation") public View onCreateActionView() { throw new UnsupportedOperationException("Use onCreateActionView(MenuItem) instead."); } @Override public View onCreateActionView(MenuItem item) { - if (mMenuItem != null || mView != null) { + if (mButton != null) { Log.e(TAG, "onCreateActionView: this ActionProvider is already associated " + "with a menu item. Don't reuse MediaRouteActionProvider instances! " + "Abandoning the old one..."); } - mMenuItem = item; - mView = new MediaRouteButton(mContext); - mView.setCheatSheetEnabled(true); - mView.setRouteTypes(mRouteTypes); - mView.setExtendedSettingsClickListener(mExtendedSettingsListener); - mView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + + mButton = new MediaRouteButton(mContext); + mButton.setCheatSheetEnabled(true); + mButton.setRouteTypes(mRouteTypes); + mButton.setExtendedSettingsClickListener(mExtendedSettingsListener); + mButton.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)); - return mView; + return mButton; } @Override public boolean onPerformDefaultAction() { - final FragmentManager fm = getActivity().getFragmentManager(); - // See if one is already attached to this activity. - MediaRouteChooserDialogFragment dialogFragment = - (MediaRouteChooserDialogFragment) fm.findFragmentByTag( - MediaRouteChooserDialogFragment.FRAGMENT_TAG); - if (dialogFragment != null) { - Log.w(TAG, "onPerformDefaultAction(): Chooser dialog already showing!"); - return false; - } - - dialogFragment = new MediaRouteChooserDialogFragment(); - dialogFragment.setExtendedSettingsClickListener(mExtendedSettingsListener); - dialogFragment.setRouteTypes(mRouteTypes); - dialogFragment.show(fm, MediaRouteChooserDialogFragment.FRAGMENT_TAG); - return true; - } - - private Activity getActivity() { - // Gross way of unwrapping the Activity so we can get the FragmentManager - Context context = mContext; - while (context instanceof ContextWrapper && !(context instanceof Activity)) { - context = ((ContextWrapper) context).getBaseContext(); - } - if (!(context instanceof Activity)) { - throw new IllegalStateException("The MediaRouteActionProvider's Context " + - "is not an Activity."); - } - - return (Activity) context; - } - - public void setExtendedSettingsClickListener(View.OnClickListener listener) { - mExtendedSettingsListener = listener; - if (mView != null) { - mView.setExtendedSettingsClickListener(listener); + if (mButton != null) { + return mButton.showDialogInternal(); } + return false; } @Override @@ -136,36 +143,43 @@ public class MediaRouteActionProvider extends ActionProvider { @Override public boolean isVisible() { - return mRouter.getRouteCount() > 1; + return mRouter.isRouteAvailable(mRouteTypes, + MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE); + } + + private void refreshRoute() { + refreshVisibility(); } - private static class RouterCallback extends MediaRouter.SimpleCallback { - private WeakReference+ * If the default route is selected or if the currently selected route does + * not match the {@link #getRouteTypes route types}, then shows the route chooser dialog. + * Otherwise, shows the route controller dialog to offer the user + * a choice to disconnect from the route or perform other control actions + * such as setting the route's volume. + *
+ * This will attach a {@link DialogFragment} to the containing Activity. + *
+ */ + public void showDialog() { + showDialogInternal(); + } + + boolean showDialogInternal() { + if (!mAttachedToWindow) { + return false; } - if (mToggleMode) { - if (mRemoteActive) { - mRouter.selectRouteInt(mRouteTypes, mRouter.getDefaultRoute()); - } else { - final int N = mRouter.getRouteCount(); - for (int i = 0; i < N; i++) { - final RouteInfo route = mRouter.getRouteAt(i); - if ((route.getSupportedTypes() & mRouteTypes) != 0 && - route != mRouter.getDefaultRoute()) { - mRouter.selectRouteInt(mRouteTypes, route); - } - } + DialogFragment f = MediaRouteDialogPresenter.showDialogFragment(getActivity(), + mRouteTypes, mExtendedSettingsClickListener); + return f != null; + } + + private Activity getActivity() { + // Gross way of unwrapping the Activity so we can get the FragmentManager + Context context = getContext(); + while (context instanceof ContextWrapper) { + if (context instanceof Activity) { + return (Activity)context; } - } else { - showDialog(); + context = ((ContextWrapper)context).getBaseContext(); } - - return handled; + throw new IllegalStateException("The MediaRouteButton's Context is not an Activity."); } + /** + * Sets whether to enable showing a toast with the content descriptor of the + * button when the button is long pressed. + */ void setCheatSheetEnabled(boolean enable) { mCheatSheetEnabled = enable; } + @Override + public boolean performClick() { + // Send the appropriate accessibility events and call listeners + boolean handled = super.performClick(); + if (!handled) { + playSoundEffect(SoundEffectConstants.CLICK); + } + return showDialogInternal() || handled; + } + @Override public boolean performLongClick() { if (super.performLongClick()) { @@ -183,85 +228,9 @@ public class MediaRouteButton extends View { } cheatSheet.show(); performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - return true; } - public void setRouteTypes(int types) { - if (types == mRouteTypes) { - // Already registered; nothing to do. - return; - } - - if (mAttachedToWindow && mRouteTypes != 0) { - mRouter.removeCallback(mRouterCallback); - } - - mRouteTypes = types; - - if (mAttachedToWindow) { - updateRouteInfo(); - mRouter.addCallback(types, mRouterCallback); - } - } - - private void updateRouteInfo() { - updateRemoteIndicator(); - updateRouteCount(); - } - - public int getRouteTypes() { - return mRouteTypes; - } - - void updateRemoteIndicator() { - final RouteInfo selected = mRouter.getSelectedRoute(mRouteTypes); - final boolean isRemote = selected != mRouter.getDefaultRoute(); - final boolean isConnecting = selected != null && - selected.getStatusCode() == RouteInfo.STATUS_CONNECTING; - - boolean needsRefresh = false; - if (mRemoteActive != isRemote) { - mRemoteActive = isRemote; - needsRefresh = true; - } - if (mIsConnecting != isConnecting) { - mIsConnecting = isConnecting; - needsRefresh = true; - } - - if (needsRefresh) { - refreshDrawableState(); - } - } - - void updateRouteCount() { - final int N = mRouter.getRouteCount(); - int count = 0; - boolean hasVideoRoutes = false; - for (int i = 0; i < N; i++) { - final RouteInfo route = mRouter.getRouteAt(i); - final int routeTypes = route.getSupportedTypes(); - if ((routeTypes & mRouteTypes) != 0) { - if (route instanceof RouteGroup) { - count += ((RouteGroup) route).getRouteCount(); - } else { - count++; - } - if ((routeTypes & MediaRouter.ROUTE_TYPE_LIVE_VIDEO) != 0) { - hasVideoRoutes = true; - } - } - } - - setEnabled(count != 0); - - // Only allow toggling if we have more than just user routes. - // Don't toggle if we support video routes, we may have to let the dialog scan. - mToggleMode = count == 2 && (mRouteTypes & MediaRouter.ROUTE_TYPE_LIVE_AUDIO) != 0 && - !hasVideoRoutes; - } - @Override protected int[] onCreateDrawableState(int extraSpace) { final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); @@ -289,6 +258,21 @@ public class MediaRouteButton extends View { } } + private void setRemoteIndicatorDrawable(Drawable d) { + if (mRemoteIndicator != null) { + mRemoteIndicator.setCallback(null); + unscheduleDrawable(mRemoteIndicator); + } + mRemoteIndicator = d; + if (d != null) { + d.setCallback(this); + d.setState(getDrawableState()); + d.setVisible(getVisibility() == VISIBLE, false); + } + + refreshDrawableState(); + } + @Override protected boolean verifyDrawable(Drawable who) { return super.verifyDrawable(who) || who == mRemoteIndicator; @@ -297,12 +281,16 @@ public class MediaRouteButton extends View { @Override public void jumpDrawablesToCurrentState() { super.jumpDrawablesToCurrentState(); - if (mRemoteIndicator != null) mRemoteIndicator.jumpToCurrentState(); + + if (mRemoteIndicator != null) { + mRemoteIndicator.jumpToCurrentState(); + } } @Override public void setVisibility(int visibility) { super.setVisibility(visibility); + if (mRemoteIndicator != null) { mRemoteIndicator.setVisible(getVisibility() == VISIBLE, false); } @@ -311,19 +299,22 @@ public class MediaRouteButton extends View { @Override public void onAttachedToWindow() { super.onAttachedToWindow(); + mAttachedToWindow = true; if (mRouteTypes != 0) { - mRouter.addCallback(mRouteTypes, mRouterCallback); - updateRouteInfo(); + mRouter.addCallback(mRouteTypes, mCallback, + MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY); } + refreshRoute(); } @Override public void onDetachedFromWindow() { + mAttachedToWindow = false; if (mRouteTypes != 0) { - mRouter.removeCallback(mRouterCallback); + mRouter.removeCallback(mCallback); } - mAttachedToWindow = false; + super.onDetachedFromWindow(); } @@ -386,93 +377,71 @@ public class MediaRouteButton extends View { final int drawLeft = left + (right - left - drawWidth) / 2; final int drawTop = top + (bottom - top - drawHeight) / 2; - mRemoteIndicator.setBounds(drawLeft, drawTop, drawLeft + drawWidth, drawTop + drawHeight); + mRemoteIndicator.setBounds(drawLeft, drawTop, + drawLeft + drawWidth, drawTop + drawHeight); mRemoteIndicator.draw(canvas); } - public void setExtendedSettingsClickListener(OnClickListener listener) { - mExtendedSettingsClickListener = listener; - if (mDialogFragment != null) { - mDialogFragment.setExtendedSettingsClickListener(listener); - } - } - - /** - * Asynchronously show the route chooser dialog. - * This will attach a {@link DialogFragment} to the containing Activity. - */ - public void showDialog() { - final FragmentManager fm = getActivity().getFragmentManager(); - if (mDialogFragment == null) { - // See if one is already attached to this activity. - mDialogFragment = (MediaRouteChooserDialogFragment) fm.findFragmentByTag( - MediaRouteChooserDialogFragment.FRAGMENT_TAG); - } - if (mDialogFragment != null) { - Log.w(TAG, "showDialog(): Already showing!"); - return; - } + private void refreshRoute() { + if (mAttachedToWindow) { + final MediaRouter.RouteInfo route = mRouter.getSelectedRoute(); + final boolean isRemote = !route.isDefault() && route.matchesTypes(mRouteTypes); + final boolean isConnecting = isRemote && route.isConnecting(); + + boolean needsRefresh = false; + if (mRemoteActive != isRemote) { + mRemoteActive = isRemote; + needsRefresh = true; + } + if (mIsConnecting != isConnecting) { + mIsConnecting = isConnecting; + needsRefresh = true; + } - mDialogFragment = new MediaRouteChooserDialogFragment(); - mDialogFragment.setExtendedSettingsClickListener(mExtendedSettingsClickListener); - mDialogFragment.setLauncherListener(new MediaRouteChooserDialogFragment.LauncherListener() { - @Override - public void onDetached(MediaRouteChooserDialogFragment detachedFragment) { - mDialogFragment = null; + if (needsRefresh) { + refreshDrawableState(); } - }); - mDialogFragment.setRouteTypes(mRouteTypes); - mDialogFragment.show(fm, MediaRouteChooserDialogFragment.FRAGMENT_TAG); - } - private Activity getActivity() { - // Gross way of unwrapping the Activity so we can get the FragmentManager - Context context = getContext(); - while (context instanceof ContextWrapper && !(context instanceof Activity)) { - context = ((ContextWrapper) context).getBaseContext(); + setEnabled(mRouter.isRouteAvailable(mRouteTypes, + MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE)); } - if (!(context instanceof Activity)) { - throw new IllegalStateException("The MediaRouteButton's Context is not an Activity."); - } - - return (Activity) context; } - private class MediaRouteCallback extends MediaRouter.SimpleCallback { + private final class MediaRouterCallback extends MediaRouter.SimpleCallback { @Override - public void onRouteSelected(MediaRouter router, int type, RouteInfo info) { - updateRemoteIndicator(); + public void onRouteAdded(MediaRouter router, RouteInfo info) { + refreshRoute(); } @Override - public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) { - updateRemoteIndicator(); + public void onRouteRemoved(MediaRouter router, RouteInfo info) { + refreshRoute(); } @Override public void onRouteChanged(MediaRouter router, RouteInfo info) { - updateRemoteIndicator(); + refreshRoute(); } @Override - public void onRouteAdded(MediaRouter router, RouteInfo info) { - updateRouteCount(); + public void onRouteSelected(MediaRouter router, int type, RouteInfo info) { + refreshRoute(); } @Override - public void onRouteRemoved(MediaRouter router, RouteInfo info) { - updateRouteCount(); + public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) { + refreshRoute(); } @Override public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group, int index) { - updateRouteCount(); + refreshRoute(); } @Override public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) { - updateRouteCount(); + refreshRoute(); } } } diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 7bcf43eb8d038867ffb05f196dd2539cebe874b1..2045ed8411a30a3e7673b7edbe9c6ec48fc36d0e 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -58,10 +58,7 @@ public class StatusBarManager { | DISABLE_SYSTEM_INFO | DISABLE_RECENT | DISABLE_HOME | DISABLE_BACK | DISABLE_CLOCK | DISABLE_SEARCH; - public static final int NAVIGATION_HINT_BACK_NOP = 1 << 0; - public static final int NAVIGATION_HINT_HOME_NOP = 1 << 1; - public static final int NAVIGATION_HINT_RECENT_NOP = 1 << 2; - public static final int NAVIGATION_HINT_BACK_ALT = 1 << 3; + public static final int NAVIGATION_HINT_BACK_ALT = 1 << 0; public static final int WINDOW_STATUS_BAR = 1; public static final int WINDOW_NAVIGATION_BAR = 2; diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index 607930c0be440ec4527ebc464cc2df7106646322..91b0d7cd68baac932d4c727f7ba1f84959b01621 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -146,7 +146,9 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { @Override public void shutdown() { synchronized (mLock) { - throwIfCalledByNotTrustedUidLocked(); + if (isConnectedLocked()) { + throwIfCalledByNotTrustedUidLocked(); + } throwIfShutdownLocked(); mIsShutdown = true; if (isConnectedLocked()) { diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl index bb4f5f160bbeb42c820051ec0e432f71aef2853f..12ee3b624f63f29a28512d3d2d8772c42680234a 100644 --- a/core/java/android/app/backup/IBackupManager.aidl +++ b/core/java/android/app/backup/IBackupManager.aidl @@ -43,14 +43,14 @@ interface IBackupManager { void dataChanged(String packageName); /** - * Erase all backed-up data for the given package from the storage + * Erase all backed-up data for the given package from the given storage * destination. * * Any application can invoke this method for its own package, but * only callers who hold the android.permission.BACKUP permission * may invoke it for arbitrary packages. */ - void clearBackupData(String packageName); + void clearBackupData(String transportName, String packageName); /** * Notifies the Backup Manager Service that an agent has become available. This diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index a9d055988862e49164bf99b47b1c565b5ad1cf99..ddde3fb00d69dd42ae160a987d1df2e234fb0084 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -398,135 +398,137 @@ public abstract class ContentProvider implements ComponentCallbacks2 { return AppOpsManager.MODE_ALLOWED; } - private void enforceReadPermissionInner(Uri uri) throws SecurityException { - final Context context = getContext(); - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - String missingPerm = null; - - if (UserHandle.isSameApp(uid, mMyUid)) { - return; + private int enforceWritePermission(String callingPkg, Uri uri) throws SecurityException { + enforceWritePermissionInner(uri); + if (mWriteOp != AppOpsManager.OP_NONE) { + return mAppOpsManager.noteOp(mWriteOp, Binder.getCallingUid(), callingPkg); } + return AppOpsManager.MODE_ALLOWED; + } + } - if (mExported) { - final String componentPerm = getReadPermission(); - if (componentPerm != null) { - if (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED) { - return; - } else { - missingPerm = componentPerm; - } + /** {@hide} */ + protected void enforceReadPermissionInner(Uri uri) throws SecurityException { + final Context context = getContext(); + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + String missingPerm = null; + + if (UserHandle.isSameApp(uid, mMyUid)) { + return; + } + + if (mExported) { + final String componentPerm = getReadPermission(); + if (componentPerm != null) { + if (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED) { + return; + } else { + missingPerm = componentPerm; } + } - // track if unprotected read is allowed; any denied - //+ * If set to 1, the HAL will always split result + * metadata for a single capture into multiple buffers, + * returned using multiple process_capture_result calls. + *
+ *+ * Does not need to be listed in static + * metadata. Support for partial results will be reworked in + * future versions of camera service. This quirk will stop + * working at that point; DO NOT USE without careful + * consideration of future support. + *
+ * + * Optional - This value may be null on some devices. + * + * @hide + */ + public static final Key* How many output streams can be allocated at diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 7095e4d498ab6d7f8aacfb48a0e8674b0a05d283..9e8d7d19e288bec6cb21767e9b0aaa841a73366b 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -630,6 +630,36 @@ public interface CameraDevice extends AutoCloseable { // default empty implementation } + /** + * This method is called when some results from an image capture are + * available. + * + *
The result provided here will contain some subset of the fields of + * a full result. Multiple onCapturePartial calls may happen per + * capture; a given result field will only be present in one partial + * capture at most. The final onCaptureCompleted call will always + * contain all the fields, whether onCapturePartial was called or + * not.
+ * + *The default implementation of this method does nothing.
+ * + * @param camera The CameraDevice sending the callback. + * @param request The request that was given to the CameraDevice + * @param result The partial output metadata from the capture, which + * includes a subset of the CaptureResult fields. + * + * @see #capture + * @see #captureBurst + * @see #setRepeatingRequest + * @see #setRepeatingBurst + * + * @hide + */ + public void onCapturePartial(CameraDevice camera, + CaptureRequest request, CaptureResult result) { + // default empty implementation + } + /** * This method is called when an image capture has completed and the * result metadata is available. diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index dbd0457e525f5c372b65508a6de47020531e4d80..535b9636769dbf21b3a8284de3a36e1f49c72104 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -589,6 +589,32 @@ public final class CaptureResult extends CameraMetadata { public static final Key+ * Whether a result given to the framework is the + * final one for the capture, or only a partial that contains a + * subset of the full set of dynamic metadata + * values. + *
+ *+ * The entries in the result metadata buffers for a + * single capture may not overlap, except for this entry. The + * FINAL buffers must retain FIFO ordering relative to the + * requests that generate them, so the FINAL buffer for frame 3 must + * always be sent to the framework after the FINAL buffer for frame 2, and + * before the FINAL buffer for frame 4. PARTIAL buffers may be returned + * in any order relative to other frames, but all PARTIAL buffers for a given + * capture must arrive before the FINAL buffer for that capture. This entry may + * only be used by the HAL if quirks.usePartialResult is set to 1. + *
+ * + * Optional - This value may be null on some devices. + * + * @hide + */ + public static final Key
* A frame counter set by the framework. This value monotonically
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index c5d0999213068cbe0439cda4b6915e4ae4b419c0..40586f0efcd966298bd7cc0e7876b0f64506e101 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -19,27 +19,24 @@ package android.hardware.camera2.impl;
import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraMetadata;
-import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.utils.CameraBinderDecorator;
import android.hardware.camera2.utils.CameraRuntimeException;
-import android.os.IBinder;
-import android.os.RemoteException;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
+import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
import android.view.Surface;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
-import java.util.Stack;
/**
* HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
@@ -49,6 +46,8 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
private final String TAG;
private final boolean DEBUG;
+ private static final int REQUEST_ID_NONE = -1;
+
// TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
private ICameraDeviceUser mRemoteDevice;
@@ -63,7 +62,8 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
private final SparseArray
+ * Calls to this method nest and must be matched by an equal number of calls to
+ * {@link #stopWifiDisplayScan()}.
+ *
+ * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}.
+ *
+ * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}.
+ *
- * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY} to connect - * to unknown displays. No permissions are required to connect to already known displays. + * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}. *
* * @param deviceAddress The MAC address of the device to which we should connect. diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 936a0867fd83a014083f436647c262b5df143341..34174301ac8eb3a75fd4e47728c4a8e4822aab79 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -72,6 +72,8 @@ public final class DisplayManagerGlobal { private final SparseArrayFor more information about using NFC, read the * Near Field Communication developer guide.
+ *To perform basic file sharing between devices, read + * Sharing Files with NFC. * */ public final class NfcAdapter { @@ -309,8 +311,12 @@ public final class NfcAdapter { final Context mContext; /** - * A callback to be invoked when the system has found a tag in - * reader mode. + * A callback to be invoked when the system finds a tag while the foreground activity is + * operating in reader mode. + *
Register your {@code ReaderCallback} implementation with {@link + * NfcAdapter#enableReaderMode} and disable it with {@link + * NfcAdapter#disableReaderMode}. + * @see NfcAdapter#enableReaderMode */ public interface ReaderCallback { public void onTagDiscovered(Tag tag); diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index dbaa325596d8cb6504c525fceb86872b44a4c6a0..9ada6e6400896781d72c962527a3cc8d5c81c690 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -105,6 +105,11 @@ public abstract class BatteryStats implements Parcelable { */ public static final int FOREGROUND_ACTIVITY = 10; + /** + * A constant indicating a wifi batched scan is active + */ + public static final int WIFI_BATCHED_SCAN = 11; + /** * Include all of the data in the stats, including previously saved data. */ @@ -270,6 +275,8 @@ public abstract class BatteryStats implements Parcelable { public abstract void noteFullWifiLockReleasedLocked(); public abstract void noteWifiScanStartedLocked(); public abstract void noteWifiScanStoppedLocked(); + public abstract void noteWifiBatchedScanStartedLocked(int csph); + public abstract void noteWifiBatchedScanStoppedLocked(); public abstract void noteWifiMulticastEnabledLocked(); public abstract void noteWifiMulticastDisabledLocked(); public abstract void noteAudioTurnedOnLocked(); @@ -281,6 +288,7 @@ public abstract class BatteryStats implements Parcelable { public abstract long getWifiRunningTime(long batteryRealtime, int which); public abstract long getFullWifiLockTime(long batteryRealtime, int which); public abstract long getWifiScanTime(long batteryRealtime, int which); + public abstract long getWifiBatchedScanTime(int csphBin, long batteryRealtime, int which); public abstract long getWifiMulticastTime(long batteryRealtime, int which); public abstract long getAudioTurnedOnTime(long batteryRealtime, int which); @@ -288,6 +296,8 @@ public abstract class BatteryStats implements Parcelable { public abstract Timer getForegroundActivityTimer(); public abstract Timer getVibratorOnTimer(); + public static final int NUM_WIFI_BATCHED_SCAN_BINS = 5; + /** * Note that these must match the constants in android.os.PowerManager. * Also, if the user activity types change, the BatteryStatsImpl.VERSION must @@ -844,12 +854,13 @@ public abstract class BatteryStats implements Parcelable { public static final int DATA_CONNECTION_EVDO_B = 12; public static final int DATA_CONNECTION_LTE = 13; public static final int DATA_CONNECTION_EHRPD = 14; - public static final int DATA_CONNECTION_OTHER = 15; + public static final int DATA_CONNECTION_HSPAP = 15; + public static final int DATA_CONNECTION_OTHER = 16; static final String[] DATA_CONNECTION_NAMES = { "none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A", "1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte", - "ehrpd", "other" + "ehrpd", "hspap", "other" }; public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER+1; @@ -2080,9 +2091,11 @@ public abstract class BatteryStats implements Parcelable { TimeUtils.formatDuration(ew.usedTime, pw); pw.print(" over "); TimeUtils.formatDuration(ew.overTime, pw); - pw.print(" ("); - pw.print((ew.usedTime*100)/ew.overTime); - pw.println("%)"); + if (ew.overTime != 0) { + pw.print(" ("); + pw.print((ew.usedTime*100)/ew.overTime); + pw.println("%)"); + } } } uidActivity = true; diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 114a1ea69035c990071a49f5337df836b78a9f3a..bc51a60992805fff2afe069bfd5e40758e77dd8d 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -457,6 +457,13 @@ public class Build { * margins correctly. *
* Note: This is for specifying orientation without media - * size. You should not use the dimensions reported by this class. + * size. You should not use the dimensions reported by this instance. *
*/ public static final MediaSize UNKNOWN_PORTRAIT = @@ -288,7 +292,7 @@ public final class PrintAttributes implements Parcelable { * Unknown media size in landscape mode. ** Note: This is for specifying orientation without media - * size. You should not use the dimensions reported by this class. + * size. You should not use the dimensions reported by this instance. *
*/ public static final MediaSize UNKNOWN_LANDSCAPE = @@ -615,9 +619,7 @@ public final class PrintAttributes implements Parcelable { private final int mHeightMils; /** - * Creates a new instance. This is the preferred constructor since - * it enables the media size label to be shown in a localized fashion - * on a locale change. + * Creates a new instance. * * @param id The unique media size id. * @param packageName The name of the creating package. @@ -625,10 +627,9 @@ public final class PrintAttributes implements Parcelable { * @param widthMils The width in mils (thousands of an inch). * @param heightMils The height in mils (thousands of an inch). * - * @throws IllegalArgumentException If the id is empty. - * @throws IllegalArgumentException If the label is empty. - * @throws IllegalArgumentException If the widthMils is less than or equal to zero. - * @throws IllegalArgumentException If the heightMils is less than or equal to zero. + * @throws IllegalArgumentException If the id is empty or the label + * is empty or the widthMils is less than or equal to zero or the + * heightMils is less than or equal to zero. * * @hide */ @@ -667,14 +668,13 @@ public final class PrintAttributes implements Parcelable { * * @param id The unique media size id. It is unique amongst other media sizes * supported by the printer. - * @param label The internationalized human readable label. + * @param label The localized human readable label. * @param widthMils The width in mils (thousands of an inch). * @param heightMils The height in mils (thousands of an inch). * - * @throws IllegalArgumentException If the id is empty. - * @throws IllegalArgumentException If the label is empty. - * @throws IllegalArgumentException If the widthMils is less than or equal to zero. - * @throws IllegalArgumentException If the heightMils is less than or equal to zero. + * @throws IllegalArgumentException If the id is empty or the label is empty + * or the widthMils is less than or equal to zero or the heightMils is less + * than or equal to zero. */ public MediaSize(String id, String label, int widthMils, int heightMils) { if (TextUtils.isEmpty(id)) { @@ -776,12 +776,16 @@ public final class PrintAttributes implements Parcelable { } /** - * Returns a new media size in a portrait orientation + * Returns a new media size instance in a portrait orientation, * which is the height is the greater dimension. * - * @return New instance in landscape orientation. + * @return New instance in landscape orientation if this one + * is in landscape, otherwise this instance. */ public MediaSize asPortrait() { + if (isPortrait()) { + return this; + } return new MediaSize(mId, mLabel, mPackageName, Math.min(mWidthMils, mHeightMils), Math.max(mWidthMils, mHeightMils), @@ -789,12 +793,16 @@ public final class PrintAttributes implements Parcelable { } /** - * Returns a new media size in a landscape orientation + * Returns a new media size instance in a landscape orientation, * which is the height is the lesser dimension. * - * @return New instance in landscape orientation. + * @return New instance in landscape orientation if this one + * is in portrait, otherwise this instance. */ public MediaSize asLandscape() { + if (!isPortrait()) { + return this; + } return new MediaSize(mId, mLabel, mPackageName, Math.max(mWidthMils, mHeightMils), Math.min(mWidthMils, mHeightMils), @@ -881,8 +889,8 @@ public final class PrintAttributes implements Parcelable { * This class specifies a supported resolution in DPI (dots per inch). * Resolution defines how many points with different color can be placed * on one inch in horizontal or vertical direction of the target media. - * For example, a printer with 600DIP can produce higher quality images - * the one with 300DPI resolution. + * For example, a printer with 600 DPI can produce higher quality images + * the one with 300 DPI resolution. */ public static final class Resolution { private final String mId; @@ -895,14 +903,13 @@ public final class PrintAttributes implements Parcelable { * * @param id The unique resolution id. It is unique amongst other resolutions * supported by the printer. - * @param label The internationalized human readable label. + * @param label The localized human readable label. * @param horizontalDpi The horizontal resolution in DPI (dots per inch). * @param verticalDpi The vertical resolution in DPI (dots per inch). * - * @throws IllegalArgumentException If the id is empty. - * @throws IllegalArgumentException If the label is empty. - * @throws IllegalArgumentException If the horizontalDpi is less than or equal to zero. - * @throws IllegalArgumentException If the verticalDpi is less than or equal to zero. + * @throws IllegalArgumentException If the id is empty or the label is empty + * or the horizontalDpi is less than or equal to zero or the verticalDpi is + * less than or equal to zero. */ public Resolution(String id, String label, int horizontalDpi, int verticalDpi) { if (TextUtils.isEmpty(id)) { diff --git a/core/java/android/print/PrintDocumentAdapter.java b/core/java/android/print/PrintDocumentAdapter.java index 4113ac737c1a43aa3349fd89379618b5b6a36cd2..1f59bef853a3047a529e652a842e43834a45d04e 100644 --- a/core/java/android/print/PrintDocumentAdapter.java +++ b/core/java/android/print/PrintDocumentAdapter.java @@ -38,15 +38,46 @@ import android.os.ParcelFileDescriptor; * *+ * The {@link #onStart()} callback is always the first call you will receive and + * is useful for doing one time setup or resource allocation before printing. You + * will not receive a subsequent call here. + *
+ *+ * The {@link #onLayout(PrintAttributes, PrintAttributes, CancellationSignal, + * LayoutResultCallback, Bundle)} callback requires that you layout the content + * based on the current {@link PrintAttributes}. The execution of this method is + * not considered completed until you invoke one of the methods on the passed in + * callback instance. Hence, you will not receive a subsequent call to any other + * method of this class until the execution of this method is complete by invoking + * one of the callback methods. + *
+ *+ * The {@link #onWrite(PageRange[], ParcelFileDescriptor, CancellationSignal, + * WriteResultCallback)} requires that you render and write the content of some + * pages to the provided destination. The execution of this method is not + * considered complete until you invoke one of the methods on the passed in + * callback instance. Hence, you will not receive a subsequent call to any other + * method of this class until the execution of this method is complete by invoking + * one of the callback methods. You will never receive a sequence of one or more + * calls to this method without a previous call to {@link #onLayout(PrintAttributes, + * PrintAttributes, CancellationSignal, LayoutResultCallback, Bundle)}. + *
+ *+ * The {@link #onFinish()} callback is always the last call you will receive and + * is useful for doing one time cleanup or resource deallocation after printing. + * You will not receive a subsequent call here. + *
* *@@ -54,7 +85,11 @@ import android.os.ParcelFileDescriptor; * of the work on an arbitrary thread. For example, if the printed content * does not depend on the UI state, i.e. on what is shown on the screen, then * you can offload the entire work on a dedicated thread, thus making your - * application interactive while the print work is being performed. + * application interactive while the print work is being performed. Note that + * while your activity is covered by the system print UI and a user cannot + * interact with it, doing the printing work on the main application thread + * may affect the performance of your other application components as they + * are also executed on that thread. *
*
* You can also do work on different threads, for example if you print UI
@@ -64,7 +99,7 @@ import android.os.ParcelFileDescriptor;
* This will ensure that the UI does not change while you are laying out the
* printed content. Then you can handle {@link #onWrite(PageRange[], ParcelFileDescriptor,
* CancellationSignal, WriteResultCallback)} and {@link #onFinish()} on another
- * thread. This will ensure that the UI is frozen for the minimal amount of
+ * thread. This will ensure that the main thread is busy for a minimal amount of
* time. Also this assumes that you will generate the printed content in
* {@link #onLayout(PrintAttributes, PrintAttributes, CancellationSignal,
* LayoutResultCallback, Bundle)} which is not mandatory. If you use multiple
@@ -76,6 +111,12 @@ public abstract class PrintDocumentAdapter {
/**
* Extra: mapped to a boolean value that is true
if
* the current layout is for a print preview, false
otherwise.
+ * This extra is provided in the {@link Bundle} argument of the {@link
+ * #onLayout(PrintAttributes, PrintAttributes, CancellationSignal,
+ * LayoutResultCallback, Bundle)} callback.
+ *
+ * @see #onLayout(PrintAttributes, PrintAttributes, CancellationSignal,
+ * LayoutResultCallback, Bundle)
*/
public static final String EXTRA_PRINT_PREVIEW = "EXTRA_PRINT_PREVIEW";
@@ -95,17 +136,41 @@ public abstract class PrintDocumentAdapter {
* After you are done laying out, you must invoke: {@link
* LayoutResultCallback#onLayoutFinished(PrintDocumentInfo, boolean)} with
* the last argument true
or false
depending on
- * whether the layout changed the content or not, respectively; and {@link
- * LayoutResultCallback#onLayoutFailed(CharSequence)}, if an error occurred.
- * Note that you must call one of the methods of the given callback.
+ * whether the layout changed the content or not, respectively; or {@link
+ * LayoutResultCallback#onLayoutFailed(CharSequence)}, if an error occurred;
+ * or {@link LayoutResultCallback#onLayoutCancelled()} if layout was
+ * cancelled in a response to a cancellation request via the passed in
+ * {@link CancellationSignal}. Note that you must call one of
+ * the methods of the given callback for this method to be considered complete
+ * which is you will not receive any calls to this adapter until the current
+ * layout operation is complete by invoking a method on the callback instance.
+ * The callback methods can be invoked from an arbitrary thread.
+ *
+ * One of the arguments passed to this method is a {@link CancellationSignal} + * which is used to propagate requests from the system to your application for + * canceling the current layout operation. For example, a cancellation may be + * requested if the user changes a print option that may affect layout while + * you are performing a layout operation. In such a case the system will make + * an attempt to cancel the current layout as another one will have to be performed. + * Typically, you should register a cancellation callback in the cancellation + * signal. The cancellation callback will not be made on the + * main thread and can be registered as follows: *
+ *+ * cancellationSignal.setOnCancelListener(new OnCancelListener() { + * @Override + * public void onCancel() { + * // Cancel layout + * } + * }); + **
* Note: If the content is large and a layout will be * performed, it is a good practice to schedule the work on a dedicated * thread and register an observer in the provided {@link * CancellationSignal} upon invocation of which you should stop the - * layout. The cancellation callback will not be made on the main - * thread. + * layout. *
* * @param oldAttributes The old print attributes. @@ -128,17 +193,41 @@ public abstract class PrintDocumentAdapter { * on the main thread. ** After you are done writing, you should close the file descriptor and - * invoke {@link WriteResultCallback #onWriteFinished(PageRange[]), if writing + * invoke {@link WriteResultCallback#onWriteFinished(PageRange[])}, if writing * completed successfully; or {@link WriteResultCallback#onWriteFailed( - * CharSequence)}, if an error occurred. Note that you must call one of - * the methods of the given callback. + * CharSequence)}, if an error occurred; or {@link WriteResultCallback#onWriteCancelled()}, + * if writing was cancelled in a response to a cancellation request via the passed + * in {@link CancellationSignal}. Note that you must call one of + * the methods of the given callback for this method to be considered complete which + * is you will not receive any calls to this adapter until the current write + * operation is complete by invoking a method on the callback instance. The callback + * methods can be invoked from an arbitrary thread. + *
+ *+ * One of the arguments passed to this method is a {@link CancellationSignal} + * which is used to propagate requests from the system to your application for + * canceling the current write operation. For example, a cancellation may be + * requested if the user changes a print option that may affect layout while + * you are performing a write operation. In such a case the system will make + * an attempt to cancel the current write as a layout will have to be performed + * which then may be followed by a write. Typically, you should register a + * cancellation callback in the cancellation signal. The cancellation callback + * will not be made on the main thread and can be registered + * as follows: *
+ *+ * cancellationSignal.setOnCancelListener(new OnCancelListener() { + * @Override + * public void onCancel() { + * // Cancel write + * } + * }); + **
* Note: If the printed content is large, it is a good * practice to schedule writing it on a dedicated thread and register an * observer in the provided {@link CancellationSignal} upon invocation of - * which you should stop writing. The cancellation callback will not be - * made on the main thread. + * which you should stop writing. *
* * @param pages The pages whose content to print - non-overlapping in ascending order. @@ -178,7 +267,8 @@ public abstract class PrintDocumentAdapter { /** * Notifies that all the data was written. * - * @param pages The pages that were written. Cannot be null or empty. + * @param pages The pages that were written. Cannot benull
+ * or empty.
*/
public void onWriteFinished(PageRange[] pages) {
/* do nothing - stub */
@@ -187,7 +277,8 @@ public abstract class PrintDocumentAdapter {
/**
* Notifies that an error occurred while writing the data.
*
- * @param error Error message. May be null if error is unknown.
+ * @param error The localized error message.
+ * shown to the user. May be null
if error is unknown.
*/
public void onWriteFailed(CharSequence error) {
/* do nothing - stub */
@@ -218,7 +309,7 @@ public abstract class PrintDocumentAdapter {
/**
* Notifies that the layout finished and whether the content changed.
*
- * @param info An info object describing the document. Cannot be null.
+ * @param info An info object describing the document. Cannot be null
.
* @param changed Whether the layout changed.
*
* @see PrintDocumentInfo
@@ -230,7 +321,8 @@ public abstract class PrintDocumentAdapter {
/**
* Notifies that an error occurred while laying out the document.
*
- * @param error Error message. May be null if error is unknown.
+ * @param error The localized error message.
+ * shown to the user. May be null
if error is unknown.
*/
public void onLayoutFailed(CharSequence error) {
/* do nothing - stub */
diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java
index b721ef4e0dfeff4994209afce71195fb5a4b363f..928be6cae90ad7f805e6cfced7c9559da369a444 100644
--- a/core/java/android/print/PrintDocumentInfo.java
+++ b/core/java/android/print/PrintDocumentInfo.java
@@ -21,12 +21,56 @@ import android.os.Parcelable;
import android.text.TextUtils;
/**
- * This class encapsulates information about a printed document.
+ * This class encapsulates information about a document for printing
+ * purposes. This meta-data is used by the platform and print services,
+ * components that interact with printers. For example, this class
+ * contains the number of pages contained in the document it describes and
+ * this number of pages is shown to the user allowing him/her to select
+ * the range to print. Also a print service may optimize the printing
+ * process based on the content type, such as document or photo.
+ * + * Instances of this class are created by the printing application and + * passed to the {@link PrintDocumentAdapter.LayoutResultCallback#onLayoutFinished( + * PrintDocumentInfo, boolean) PrintDocumentAdapter.LayoutResultCallback.onLayoutFinished( + * PrintDocumentInfo, boolean)} callback after successfully laying out the + * content which is performed in {@link PrintDocumentAdapter#onLayout(PrintAttributes, + * PrintAttributes, android.os.CancellationSignal, PrintDocumentAdapter.LayoutResultCallback, + * android.os.Bundle) PrintDocumentAdapter.onLayout(PrintAttributes, + * PrintAttributes, android.os.CancellationSignal, + * PrintDocumentAdapter.LayoutResultCallback, android.os.Bundle)}. + *
+ *+ * An example usage looks like this: + *
+ * + * . . . + * + * public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, + * CancellationSignal cancellationSignal, LayoutResultCallback callback, + * Bundle metadata) { + * + * // Assume the app defined a LayoutResult class which contains + * // the layout result data and that the content is a document. + * LayoutResult result = doSomeLayoutWork(); + * + * PrintDocumentInfo info = new PrintDocumentInfo + * .Builder("printed_file.pdf") + * .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT) + * .setPageCount(result.getPageCount()) + * .build(); + * + * callback.onLayoutFinished(info, result.getContentChanged()); + * } + * + * . . . + * + *+ * */ public final class PrintDocumentInfo implements Parcelable { /** - * Constant for unknown page count.. + * Constant for unknown page count. */ public static final int PAGE_COUNT_UNKNOWN = -1; @@ -37,11 +81,23 @@ public final class PrintDocumentInfo implements Parcelable { /** * Content type: document. + *
+ * A print service may use normal paper to print the content instead + * of dedicated photo paper. Also it may use a lower quality printing + * process as the content is not as sensitive to print quality variation + * as a photo is. + *
*/ public static final int CONTENT_TYPE_DOCUMENT = 0; /** * Content type: photo. + *+ * A print service may use dedicated photo paper to print the content + * instead of normal paper. Also it may use a higher quality printing + * process as the content is more sensitive to print quality variation + * than a document. + *
*/ public static final int CONTENT_TYPE_PHOTO = 1; @@ -82,7 +138,8 @@ public final class PrintDocumentInfo implements Parcelable { } /** - * Gets the document name. + * Gets the document name. This name may be shown to + * the user. * * @return The document name. */ @@ -213,20 +270,23 @@ public final class PrintDocumentInfo implements Parcelable { } /** - * Builder for creating an {@link PrintDocumentInfo}. + * Builder for creating a {@link PrintDocumentInfo}. */ public static final class Builder { private final PrintDocumentInfo mPrototype; /** * Constructor. + * *- * The values of the relevant properties are initialized with default - * values. Please refer to the documentation of the individual setters - * for information about the default values. + * The values of the relevant properties are initialized with defaults. + * Please refer to the documentation of the individual setters for + * information about the default values. *
* - * @param name The document name. Cannot be empty. + * @param name The document name which may be shown to the user and + * is the file name if the content it describes is saved as a PDF. + * Cannot be empty. */ public Builder(String name) { if (TextUtils.isEmpty(name)) { diff --git a/core/java/android/print/PrintJob.java b/core/java/android/print/PrintJob.java index 535ae43354fa07809e41b424962e11b1d5c3dac9..0abe2193249ee8bafefc15a03b98bf581ba4974e 100644 --- a/core/java/android/print/PrintJob.java +++ b/core/java/android/print/PrintJob.java @@ -17,8 +17,13 @@ package android.print; /** - * This class represents a print job from the perspective of - * an application. + * This class represents a print job from the perspective of an + * application. It contains behavior methods for performing operations + * on it as well as methods for querying its state. A snapshot of the + * print job state is represented by the {@link PrintJobInfo} class. + * The state of a print job may change over time. An application receives + * instances of this class when creating a print job or querying for + * its print jobs. */ public final class PrintJob { @@ -145,11 +150,12 @@ public final class PrintJob { /** * Gets whether this print job is failed. Such a print job is * not successfully printed due to an error. You can request - * a restart via {@link #restart()}. + * a restart via {@link #restart()} or cancel via {@link #cancel()}. * * @return Whether the print job is failed. * * @see #restart() + * @see #cancel() */ public boolean isFailed() { return getInfo().getState() == PrintJobInfo.STATE_FAILED; diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java index c6f0a6848f91a7514fcf97c5854db42530fc526c..63f94fe642fb2ccd3d8455fb40c9ad82eb31c2c9 100644 --- a/core/java/android/print/PrintJobInfo.java +++ b/core/java/android/print/PrintJobInfo.java @@ -16,13 +16,17 @@ package android.print; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import java.util.Arrays; /** - * This class represents the description of a print job. + * This class represents the description of a print job. The print job + * state includes properties such as its id, print attributes used for + * generating the content, and so on. Note that the print jobs state may + * change over time and this class represents a snapshot of this state. */ public final class PrintJobInfo implements Parcelable { @@ -93,7 +97,7 @@ public final class PrintJobInfo implements Parcelable { public static final int STATE_BLOCKED = 4; /** - * Print job state: The print job was successfully printed. + * Print job state: The print job is successfully printed. * This is a terminal state. ** Next valid states: None @@ -103,15 +107,14 @@ public final class PrintJobInfo implements Parcelable { /** * Print job state: The print job was printing but printing failed. - * This is a terminal state. *
- * Next valid states: None + * Next valid states: {@link #STATE_CANCELED}, {@link #STATE_STARTED} *
*/ public static final int STATE_FAILED = 6; /** - * Print job state: The print job was canceled. + * Print job state: The print job is canceled. * This is a terminal state. *
* Next valid states: None
@@ -158,6 +161,9 @@ public final class PrintJobInfo implements Parcelable {
/** Information about the printed document. */
private PrintDocumentInfo mDocumentInfo;
+ /** Advanced printer specific options. */
+ private Bundle mAdvancedOptions;
+
/** Whether we are trying to cancel this print job. */
private boolean mCanceling;
@@ -182,6 +188,7 @@ public final class PrintJobInfo implements Parcelable {
mAttributes = other.mAttributes;
mDocumentInfo = other.mDocumentInfo;
mCanceling = other.mCanceling;
+ mAdvancedOptions = other.mAdvancedOptions;
}
private PrintJobInfo(Parcel parcel) {
@@ -195,20 +202,17 @@ public final class PrintJobInfo implements Parcelable {
mCreationTime = parcel.readLong();
mCopies = parcel.readInt();
mStateReason = parcel.readString();
- if (parcel.readInt() == 1) {
- Parcelable[] parcelables = parcel.readParcelableArray(null);
+ Parcelable[] parcelables = parcel.readParcelableArray(null);
+ if (parcelables != null) {
mPageRanges = new PageRange[parcelables.length];
for (int i = 0; i < parcelables.length; i++) {
mPageRanges[i] = (PageRange) parcelables[i];
}
}
- if (parcel.readInt() == 1) {
- mAttributes = PrintAttributes.CREATOR.createFromParcel(parcel);
- }
- if (parcel.readInt() == 1) {
- mDocumentInfo = PrintDocumentInfo.CREATOR.createFromParcel(parcel);
- }
+ mAttributes = (PrintAttributes) parcel.readParcelable(null);
+ mDocumentInfo = (PrintDocumentInfo) parcel.readParcelable(null);
mCanceling = (parcel.readInt() == 1);
+ mAdvancedOptions = parcel.readBundle();
}
/**
@@ -297,6 +301,14 @@ public final class PrintJobInfo implements Parcelable {
* Gets the current job state.
*
* @return The job state.
+ *
+ * @see #STATE_CREATED
+ * @see #STATE_QUEUED
+ * @see #STATE_STARTED
+ * @see #STATE_COMPLETED
+ * @see #STATE_BLOCKED
+ * @see #STATE_FAILED
+ * @see #STATE_CANCELED
*/
public int getState() {
return mState;
@@ -511,6 +523,71 @@ public final class PrintJobInfo implements Parcelable {
mCanceling = cancelling;
}
+ /**
+ * Gets whether this job has a given advanced (printer specific) print
+ * option.
+ *
+ * @param key The option key.
+ * @return Whether the option is present.
+ *
+ * @hide
+ */
+ public boolean hasAdvancedOption(String key) {
+ return mAdvancedOptions != null && mAdvancedOptions.containsKey(key);
+ }
+
+ /**
+ * Gets the value of an advanced (printer specific) print option.
+ *
+ * @param key The option key.
+ * @return The option value.
+ *
+ * @hide
+ */
+ public String getAdvancedStringOption(String key) {
+ if (mAdvancedOptions != null) {
+ return mAdvancedOptions.getString(key);
+ }
+ return null;
+ }
+
+ /**
+ * Gets the value of an advanced (printer specific) print option.
+ *
+ * @param key The option key.
+ * @return The option value.
+ *
+ * @hide
+ */
+ public int getAdvancedIntOption(String key) {
+ if (mAdvancedOptions != null) {
+ return mAdvancedOptions.getInt(key);
+ }
+ return 0;
+ }
+
+ /**
+ * Gets the advanced options.
+ *
+ * @return The advanced options.
+ *
+ * @hide
+ */
+ public Bundle getAdvancedOptions() {
+ return mAdvancedOptions;
+ }
+
+ /**
+ * Sets the advanced options.
+ *
+ * @param options The advanced options.
+ *
+ * @hide
+ */
+ public void setAdvancedOptions(Bundle options) {
+ mAdvancedOptions = options;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -528,25 +605,11 @@ public final class PrintJobInfo implements Parcelable {
parcel.writeLong(mCreationTime);
parcel.writeInt(mCopies);
parcel.writeString(mStateReason);
- if (mPageRanges != null) {
- parcel.writeInt(1);
- parcel.writeParcelableArray(mPageRanges, flags);
- } else {
- parcel.writeInt(0);
- }
- if (mAttributes != null) {
- parcel.writeInt(1);
- mAttributes.writeToParcel(parcel, flags);
- } else {
- parcel.writeInt(0);
- }
- if (mDocumentInfo != null) {
- parcel.writeInt(1);
- mDocumentInfo.writeToParcel(parcel, flags);
- } else {
- parcel.writeInt(0);
- }
+ parcel.writeParcelableArray(mPageRanges, flags);
+ parcel.writeParcelable(mAttributes, flags);
+ parcel.writeParcelable(mDocumentInfo, 0);
parcel.writeInt(mCanceling ? 1 : 0);
+ parcel.writeBundle(mAdvancedOptions);
}
@Override
@@ -567,6 +630,7 @@ public final class PrintJobInfo implements Parcelable {
builder.append(", cancelling: " + mCanceling);
builder.append(", pages: " + (mPageRanges != null
? Arrays.toString(mPageRanges) : null));
+ builder.append(", hasAdvancedOptions: " + (mAdvancedOptions != null));
builder.append("}");
return builder.toString();
}
@@ -611,7 +675,7 @@ public final class PrintJobInfo implements Parcelable {
* Constructor.
*
* @param prototype Prototype to use as a starting point.
- * Can be null.
+ * Can be null
.
*/
public Builder(PrintJobInfo prototype) {
mPrototype = (prototype != null)
@@ -653,7 +717,10 @@ public final class PrintJobInfo implements Parcelable {
* @param value The option value.
*/
public void putAdvancedOption(String key, String value) {
-
+ if (mPrototype.mAdvancedOptions == null) {
+ mPrototype.mAdvancedOptions = new Bundle();
+ }
+ mPrototype.mAdvancedOptions.putString(key, value);
}
/**
@@ -663,7 +730,10 @@ public final class PrintJobInfo implements Parcelable {
* @param value The option value.
*/
public void putAdvancedOption(String key, int value) {
-
+ if (mPrototype.mAdvancedOptions == null) {
+ mPrototype.mAdvancedOptions = new Bundle();
+ }
+ mPrototype.mAdvancedOptions.putInt(key, value);
}
/**
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index dbd8278d3a1fbee47410826d32fc8a6a30878a78..d1bb8fd20a4c9cf613feee11b5763427fb2c6152 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -16,6 +16,8 @@
package android.print;
+import android.app.Activity;
+import android.app.Application.ActivityLifecycleCallbacks;
import android.content.Context;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
@@ -53,6 +55,49 @@ import java.util.Map;
* PrintManager printManager =
* (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
*
+ *
+ *
+ * The key idea behind printing on the platform is that the content to be printed + * should be laid out for the currently selected print options resulting in an + * optimized output and higher user satisfaction. To achieve this goal the platform + * declares a contract that the printing application has to follow which is defined + * by the {@link PrintDocumentAdapter} class. At a higher level the contract is that + * when the user selects some options from the print UI that may affect the way + * content is laid out, for example page size, the application receives a callback + * allowing it to layout the content to better fit these new constraints. After a + * layout pass the system may ask the application to render one or more pages one + * or more times. For example, an application may produce a single column list for + * smaller page sizes and a multi-column table for larger page sizes. + *
+ *+ * Print jobs are started by calling the {@link #print(String, PrintDocumentAdapter, + * PrintAttributes)} from an activity which results in bringing up the system print + * UI. Once the print UI is up, when the user changes a selected print option that + * affects the way content is laid out the system starts to interact with the + * application following the mechanics described the section above. + *
+ *+ * Print jobs can be in {@link PrintJobInfo#STATE_CREATED created}, {@link + * PrintJobInfo#STATE_QUEUED queued}, {@link PrintJobInfo#STATE_STARTED started}, + * {@link PrintJobInfo#STATE_BLOCKED blocked}, {@link PrintJobInfo#STATE_COMPLETED + * completed}, {@link PrintJobInfo#STATE_FAILED failed}, and {@link + * PrintJobInfo#STATE_CANCELED canceled} state. Print jobs are stored in dedicated + * system spooler until they are handled which is they are cancelled or completed. + * Active print jobs, ones that are not cancelled or completed, are considered failed + * if the device reboots as the new boot may be after a very long time. The user may + * choose to restart such print jobs. Once a print job is queued all relevant content + * is stored in the system spooler and its lifecycle becomes detached from this of + * the application that created it. + *
+ *+ * An applications can query the print spooler for current print jobs it created + * but not print jobs created by other applications. + *
+ * + * @see PrintJob + * @see PrintJobInfo */ public final class PrintManager { @@ -290,20 +335,54 @@ public final class PrintManager { /** * Creates a print job for printing a {@link PrintDocumentAdapter} with * default print attributes. - * - * @param printJobName A name for the new print job. + *+ * Calling this method brings the print UI allowing the user to customize + * the print job and returns a {@link PrintJob} object without waiting for the + * user to customize or confirm the print job. The returned print job instance + * is in a {@link PrintJobInfo#STATE_CREATED created} state. + *
+ * This method can be called only from an {@link Activity}. The rationale is that + * printing from a service will create an inconsistent user experience as the print + * UI would appear without any context. + *
+ *+ * Also the passed in {@link PrintDocumentAdapter} will be considered invalid if + * your activity is finished. The rationale is that once the activity that + * initiated printing is finished, the provided adapter may be in an inconsistent + * state as it may depend on the UI presented by the activity. + *
+ *+ * The default print attributes are a hint to the system how the data is to + * be printed. For example, a photo editor may look at the photo aspect ratio + * to determine the default orientation and provide a hint whether the printing + * should be in portrait or landscape. The system will do a best effort to + * selected the hinted options in the print dialog, given the current printer + * supports them. + *
+ * + * @param printJobName A name for the new print job which is shown to the user. * @param documentAdapter An adapter that emits the document to print. - * @param attributes The default print job attributes. + * @param attributes The default print job attributes ornull
.
* @return The created print job on success or null on failure.
+ * @throws IllegalStateException If not called from an {@link Activity}.
+ * @throws IllegalArgumentException If the print job name is empty or the
+ * document adapter is null.
+ *
* @see PrintJob
*/
public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter,
PrintAttributes attributes) {
+ if (!(mContext instanceof Activity)) {
+ throw new IllegalStateException("Can print only from an activity");
+ }
if (TextUtils.isEmpty(printJobName)) {
- throw new IllegalArgumentException("priintJobName cannot be empty");
+ throw new IllegalArgumentException("printJobName cannot be empty");
+ }
+ if (documentAdapter == null) {
+ throw new IllegalArgumentException("documentAdapter cannot be null");
}
- PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(documentAdapter,
- mContext.getMainLooper());
+ PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(
+ (Activity) mContext, documentAdapter);
try {
Bundle result = mService.print(printJobName, delegate,
attributes, mContext.getPackageName(), mAppId, mUserId);
@@ -369,17 +448,21 @@ public final class PrintManager {
return new PrinterDiscoverySession(mService, mContext, mUserId);
}
- private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub {
+ private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub
+ implements ActivityLifecycleCallbacks {
private final Object mLock = new Object();
private CancellationSignal mLayoutOrWriteCancellation;
- private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK -
- // cleared in finish()
+ private Activity mActivity; // Strong reference OK - cleared in finish()
+
+ private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in finish
private Handler mHandler; // Strong reference OK - cleared in finish()
+ private IPrintDocumentAdapterObserver mObserver; // Strong reference OK - cleared in finish
+
private LayoutSpec mLastLayoutSpec;
private WriteSpec mLastWriteSpec;
@@ -390,16 +473,39 @@ public final class PrintManager {
private boolean mFinishRequested;
private boolean mFinished;
- public PrintDocumentAdapterDelegate(PrintDocumentAdapter documentAdapter, Looper looper) {
+ private boolean mDestroyed;
+
+ public PrintDocumentAdapterDelegate(Activity activity,
+ PrintDocumentAdapter documentAdapter) {
+ mActivity = activity;
mDocumentAdapter = documentAdapter;
- mHandler = new MyHandler(looper);
+ mHandler = new MyHandler(mActivity.getMainLooper());
+ mActivity.getApplication().registerActivityLifecycleCallbacks(this);
+ }
+
+ @Override
+ public void setObserver(IPrintDocumentAdapterObserver observer) {
+ final boolean destroyed;
+ synchronized (mLock) {
+ if (!mDestroyed) {
+ mObserver = observer;
+ }
+ destroyed = mDestroyed;
+ }
+ if (destroyed) {
+ try {
+ observer.onDestroy();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error announcing destroyed state", re);
+ }
+ }
}
@Override
public void start() {
synchronized (mLock) {
- // Started or finished - nothing to do.
- if (mStartReqeusted || mFinishRequested) {
+ // Started called or finish called or destroyed - nothing to do.
+ if (mStartReqeusted || mFinishRequested || mDestroyed) {
return;
}
@@ -412,71 +518,85 @@ public final class PrintManager {
@Override
public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
ILayoutResultCallback callback, Bundle metadata, int sequence) {
+ final boolean destroyed;
synchronized (mLock) {
- // Start not called or finish called - nothing to do.
- if (!mStartReqeusted || mFinishRequested) {
- return;
- }
-
- // Layout cancels write and overrides layout.
- if (mLastWriteSpec != null) {
- IoUtils.closeQuietly(mLastWriteSpec.fd);
- mLastWriteSpec = null;
- }
+ destroyed = mDestroyed;
+ // If start called and not finished called and not destroyed - do some work.
+ if (mStartReqeusted && !mFinishRequested && !mDestroyed) {
+ // Layout cancels write and overrides layout.
+ if (mLastWriteSpec != null) {
+ IoUtils.closeQuietly(mLastWriteSpec.fd);
+ mLastWriteSpec = null;
+ }
- mLastLayoutSpec = new LayoutSpec();
- mLastLayoutSpec.callback = callback;
- mLastLayoutSpec.oldAttributes = oldAttributes;
- mLastLayoutSpec.newAttributes = newAttributes;
- mLastLayoutSpec.metadata = metadata;
- mLastLayoutSpec.sequence = sequence;
+ mLastLayoutSpec = new LayoutSpec();
+ mLastLayoutSpec.callback = callback;
+ mLastLayoutSpec.oldAttributes = oldAttributes;
+ mLastLayoutSpec.newAttributes = newAttributes;
+ mLastLayoutSpec.metadata = metadata;
+ mLastLayoutSpec.sequence = sequence;
+
+ // Cancel the previous cancellable operation.When the
+ // cancellation completes we will do the pending work.
+ if (cancelPreviousCancellableOperationLocked()) {
+ return;
+ }
- // Cancel the previous cancellable operation.When the
- // cancellation completes we will do the pending work.
- if (cancelPreviousCancellableOperationLocked()) {
- return;
+ doPendingWorkLocked();
+ }
+ }
+ if (destroyed) {
+ try {
+ callback.onLayoutFailed(null, sequence);
+ } catch (RemoteException re) {
+ Log.i(LOG_TAG, "Error notifying for cancelled layout", re);
}
-
- doPendingWorkLocked();
}
}
@Override
public void write(PageRange[] pages, ParcelFileDescriptor fd,
IWriteResultCallback callback, int sequence) {
+ final boolean destroyed;
synchronized (mLock) {
- // Start not called or finish called - nothing to do.
- if (!mStartReqeusted || mFinishRequested) {
- return;
- }
+ destroyed = mDestroyed;
+ // If start called and not finished called and not destroyed - do some work.
+ if (mStartReqeusted && !mFinishRequested && !mDestroyed) {
+ // Write cancels previous writes.
+ if (mLastWriteSpec != null) {
+ IoUtils.closeQuietly(mLastWriteSpec.fd);
+ mLastWriteSpec = null;
+ }
- // Write cancels previous writes.
- if (mLastWriteSpec != null) {
- IoUtils.closeQuietly(mLastWriteSpec.fd);
- mLastWriteSpec = null;
- }
+ mLastWriteSpec = new WriteSpec();
+ mLastWriteSpec.callback = callback;
+ mLastWriteSpec.pages = pages;
+ mLastWriteSpec.fd = fd;
+ mLastWriteSpec.sequence = sequence;
- mLastWriteSpec = new WriteSpec();
- mLastWriteSpec.callback = callback;
- mLastWriteSpec.pages = pages;
- mLastWriteSpec.fd = fd;
- mLastWriteSpec.sequence = sequence;
+ // Cancel the previous cancellable operation.When the
+ // cancellation completes we will do the pending work.
+ if (cancelPreviousCancellableOperationLocked()) {
+ return;
+ }
- // Cancel the previous cancellable operation.When the
- // cancellation completes we will do the pending work.
- if (cancelPreviousCancellableOperationLocked()) {
- return;
+ doPendingWorkLocked();
+ }
+ }
+ if (destroyed) {
+ try {
+ callback.onWriteFailed(null, sequence);
+ } catch (RemoteException re) {
+ Log.i(LOG_TAG, "Error notifying for cancelled write", re);
}
-
- doPendingWorkLocked();
}
}
@Override
public void finish() {
synchronized (mLock) {
- // Start not called or finish called - nothing to do.
- if (!mStartReqeusted || mFinishRequested) {
+ // Start not called or finish called or destroyed - nothing to do.
+ if (!mStartReqeusted || mFinishRequested || mDestroyed) {
return;
}
@@ -495,15 +615,90 @@ public final class PrintManager {
}
}
+ @Override
+ public void cancel() {
+ // Start not called or finish called or destroyed - nothing to do.
+ if (!mStartReqeusted || mFinishRequested || mDestroyed) {
+ return;
+ }
+ // Request cancellation of pending work if needed.
+ synchronized (mLock) {
+ cancelPreviousCancellableOperationLocked();
+ }
+ }
+
+ @Override
+ public void onActivityPaused(Activity activity) {
+ /* do nothing */
+ }
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+ /* do nothing */
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {
+ /* do nothing */
+ }
+
+ @Override
+ public void onActivityResumed(Activity activity) {
+ /* do nothing */
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ /* do nothing */
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+ /* do nothing */
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ // We really care only if the activity is being destroyed to
+ // notify the the print spooler so it can close the print dialog.
+ // Note the the spooler has a death recipient that observes if
+ // this process gets killed so we cover the case of onDestroy not
+ // being called due to this process being killed to reclaim memory.
+ final IPrintDocumentAdapterObserver observer;
+ synchronized (mLock) {
+ if (activity == mActivity) {
+ mDestroyed = true;
+ observer = mObserver;
+ clearLocked();
+ } else {
+ observer = null;
+ activity = null;
+ }
+ }
+ if (observer != null) {
+ activity.getApplication().unregisterActivityLifecycleCallbacks(
+ PrintDocumentAdapterDelegate.this);
+ try {
+ observer.onDestroy();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error announcing destroyed state", re);
+ }
+ }
+ }
+
private boolean isFinished() {
return mDocumentAdapter == null;
}
- private void doFinish() {
+ private void clearLocked() {
+ mActivity = null;
mDocumentAdapter = null;
mHandler = null;
- synchronized (mLock) {
- mLayoutOrWriteCancellation = null;
+ mLayoutOrWriteCancellation = null;
+ mLastLayoutSpec = null;
+ if (mLastWriteSpec != null) {
+ IoUtils.closeQuietly(mLastWriteSpec.fd);
+ mLastWriteSpec = null;
}
}
@@ -564,63 +759,81 @@ public final class PrintManager {
}
switch (message.what) {
case MSG_START: {
- mDocumentAdapter.onStart();
- }
- break;
+ final PrintDocumentAdapter adapter;
+ synchronized (mLock) {
+ adapter = mDocumentAdapter;
+ }
+ if (adapter != null) {
+ adapter.onStart();
+ }
+ } break;
case MSG_LAYOUT: {
+ final PrintDocumentAdapter adapter;
final CancellationSignal cancellation;
final LayoutSpec layoutSpec;
synchronized (mLock) {
+ adapter = mDocumentAdapter;
layoutSpec = mLastLayoutSpec;
mLastLayoutSpec = null;
cancellation = new CancellationSignal();
mLayoutOrWriteCancellation = cancellation;
}
- if (layoutSpec != null) {
+ if (layoutSpec != null && adapter != null) {
if (DEBUG) {
Log.i(LOG_TAG, "Performing layout");
}
- mDocumentAdapter.onLayout(layoutSpec.oldAttributes,
+ adapter.onLayout(layoutSpec.oldAttributes,
layoutSpec.newAttributes, cancellation,
new MyLayoutResultCallback(layoutSpec.callback,
layoutSpec.sequence), layoutSpec.metadata);
}
- }
- break;
+ } break;
case MSG_WRITE: {
+ final PrintDocumentAdapter adapter;
final CancellationSignal cancellation;
final WriteSpec writeSpec;
synchronized (mLock) {
+ adapter = mDocumentAdapter;
writeSpec = mLastWriteSpec;
mLastWriteSpec = null;
cancellation = new CancellationSignal();
mLayoutOrWriteCancellation = cancellation;
}
- if (writeSpec != null) {
+ if (writeSpec != null && adapter != null) {
if (DEBUG) {
Log.i(LOG_TAG, "Performing write");
}
- mDocumentAdapter.onWrite(writeSpec.pages, writeSpec.fd,
+ adapter.onWrite(writeSpec.pages, writeSpec.fd,
cancellation, new MyWriteResultCallback(writeSpec.callback,
writeSpec.fd, writeSpec.sequence));
}
- }
- break;
+ } break;
case MSG_FINISH: {
if (DEBUG) {
Log.i(LOG_TAG, "Performing finish");
}
- mDocumentAdapter.onFinish();
- doFinish();
- }
- break;
+ final PrintDocumentAdapter adapter;
+ final Activity activity;
+ synchronized (mLock) {
+ adapter = mDocumentAdapter;
+ activity = mActivity;
+ clearLocked();
+ }
+ if (adapter != null) {
+ adapter.onFinish();
+ }
+ if (activity != null) {
+ activity.getApplication().unregisterActivityLifecycleCallbacks(
+ PrintDocumentAdapterDelegate.this);
+ }
+ } break;
default: {
throw new IllegalArgumentException("Unknown message: "
@@ -647,6 +860,11 @@ public final class PrintManager {
}
final ILayoutResultCallback callback;
synchronized (mLock) {
+ if (mDestroyed) {
+ Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
+ + "finish the printing activity before print completion?");
+ return;
+ }
callback = mCallback;
clearLocked();
}
@@ -663,6 +881,11 @@ public final class PrintManager {
public void onLayoutFailed(CharSequence error) {
final ILayoutResultCallback callback;
synchronized (mLock) {
+ if (mDestroyed) {
+ Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
+ + "finish the printing activity before print completion?");
+ return;
+ }
callback = mCallback;
clearLocked();
}
@@ -678,6 +901,11 @@ public final class PrintManager {
@Override
public void onLayoutCancelled() {
synchronized (mLock) {
+ if (mDestroyed) {
+ Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
+ + "finish the printing activity before print completion?");
+ return;
+ }
clearLocked();
}
}
@@ -705,6 +933,11 @@ public final class PrintManager {
public void onWriteFinished(PageRange[] pages) {
final IWriteResultCallback callback;
synchronized (mLock) {
+ if (mDestroyed) {
+ Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
+ + "finish the printing activity before print completion?");
+ return;
+ }
callback = mCallback;
clearLocked();
}
@@ -727,6 +960,11 @@ public final class PrintManager {
public void onWriteFailed(CharSequence error) {
final IWriteResultCallback callback;
synchronized (mLock) {
+ if (mDestroyed) {
+ Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
+ + "finish the printing activity before print completion?");
+ return;
+ }
callback = mCallback;
clearLocked();
}
@@ -742,6 +980,11 @@ public final class PrintManager {
@Override
public void onWriteCancelled() {
synchronized (mLock) {
+ if (mDestroyed) {
+ Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
+ + "finish the printing activity before print completion?");
+ return;
+ }
clearLocked();
}
}
diff --git a/core/java/android/print/PrinterCapabilitiesInfo.java b/core/java/android/print/PrinterCapabilitiesInfo.java
index df51ec10fb7900bfdd6cf776f30772584d1cb23b..b615600d33ae83319f81e420f24009f715bffab2 100644
--- a/core/java/android/print/PrinterCapabilitiesInfo.java
+++ b/core/java/android/print/PrinterCapabilitiesInfo.java
@@ -24,10 +24,17 @@ import android.print.PrintAttributes.Resolution;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
/**
- * This class represents the capabilities of a printer.
+ * This class represents the capabilities of a printer. Instances
+ * of this class are created by a print service to report the
+ * capabilities of a printer it manages. The capabilities of a
+ * printer specify how it can print content. For example, what
+ * are the media sizes supported by the printer, what are the
+ * minimal margins of the printer based on its technical design,
+ * etc.
*/
public final class PrinterCapabilitiesInfo implements Parcelable {
/**
@@ -112,7 +119,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
* @return The media sizes.
*/
public List
* Look at the individual methods for a reference whether a property is
* required or if it is optional.
@@ -369,9 +377,9 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
/**
* Creates a new instance.
*
- * @param printerId The printer id. Cannot be null.
+ * @param printerId The printer id. Cannot be null
.
*
- * @throws IllegalArgumentException If the printer id is null.
+ * @throws IllegalArgumentException If the printer id is null
.
*/
public Builder(PrinterId printerId) {
if (printerId == null) {
@@ -492,7 +500,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
/**
* Crates a new {@link PrinterCapabilitiesInfo} enforcing that all
- * required properties have need specified. See individual methods
+ * required properties have been specified. See individual methods
* in this class for reference about required attributes.
*
* @return A new {@link PrinterCapabilitiesInfo}.
@@ -521,7 +529,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
if (mPrototype.mMinMargins == null) {
throw new IllegalArgumentException("margins cannot be null");
}
- return new PrinterCapabilitiesInfo(mPrototype);
+ return mPrototype;
}
private void throwIfDefaultAlreadySpecified(int propertyIndex) {
diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java
index ad79a38acb97a96595f1a0377f7c042a1c3a91d2..7fcc81f410068d28f37f4742345615ef4f905a62 100644
--- a/core/java/android/print/PrinterInfo.java
+++ b/core/java/android/print/PrinterInfo.java
@@ -21,7 +21,12 @@ import android.os.Parcelable;
import android.text.TextUtils;
/**
- * This class represents the description of a printer.
+ * This class represents the description of a printer. Instances of
+ * this class are created by print services to report to the system
+ * the printers they manage. The information of this class has two
+ * major components, printer properties such as name, id, status,
+ * description and printer capabilities which describe the various
+ * print modes a printer supports such as media sizes, margins, etc.
*/
public final class PrinterInfo implements Parcelable {
@@ -96,6 +101,10 @@ public final class PrinterInfo implements Parcelable {
* Gets the printer status.
*
* @return The status.
+ *
+ * @see #STATUS_BUSY
+ * @see #STATUS_IDLE
+ * @see #STATUS_UNAVAILABLE
*/
public int getStatus() {
return mStatus;
@@ -216,6 +225,8 @@ public final class PrinterInfo implements Parcelable {
* @param printerId The printer id. Cannot be null.
* @param name The printer name. Cannot be empty.
* @param status The printer status. Must be a valid status.
+ * @throws IllegalArgumentException If the printer id is null, or the
+ * printer name is empty or the status is not a valid one.
*/
public Builder(PrinterId printerId, String name, int status) {
if (printerId == null) {
@@ -259,7 +270,8 @@ public final class PrinterInfo implements Parcelable {
}
/**
- * Sets the printer name.
+ * Sets the localized printer name which
+ * is shown to the user
*
* @param name The name.
* @return This builder.
@@ -270,7 +282,8 @@ public final class PrinterInfo implements Parcelable {
}
/**
- * Sets the printer description.
+ * Sets the localized printer description
+ * which is shown to the user
*
* @param description The description.
* @return This builder.
@@ -292,12 +305,12 @@ public final class PrinterInfo implements Parcelable {
}
/**
- * Crates a new {@link PrinterInfo}.
+ * Creates a new {@link PrinterInfo}.
*
* @return A new {@link PrinterInfo}.
*/
public PrinterInfo build() {
- return new PrinterInfo(mPrototype);
+ return mPrototype;
}
private boolean isValidStatus(int status) {
diff --git a/core/java/android/print/package.html b/core/java/android/print/package.html
new file mode 100644
index 0000000000000000000000000000000000000000..579567d686bb90b4c52f499ce4bae19ee3a52613
--- /dev/null
+++ b/core/java/android/print/package.html
@@ -0,0 +1,46 @@
+
+
+Provides classes for implementing print support in applications and also contains all +base classes and abstractions involved in printing. These base classes are also used +by other more specialized printing related packages. +
++The entry point for interacting with the print system is the {@link android.print.PrintManager} +which is a system service that can be obtained from the current context. The print manager +provides APIs for printing, querying the state of print jobs, etc. +
++An application that wants to implement printing must extend +{@link android.print.PrintDocumentAdapter} which defines the contract between the system +and the application.The key idea behind this adapter is that the printed content may change +based on the selected print options, such as media size, orientation, which +requires the content to be re-laid out. The constraints according to which the content has +to be laid out are encapsulated in the {@link android.print.PrintAttributes} class. Once +layout is completed the application calls back to the system passing a +{@link android.print.PrintDocumentInfo} instance which describes the generated content. After +the content has been laid out the application may be asked to render some pages of that content +for preview or printing. The range of pages that have to be rendered is abstracted by the +{@link android.print.PageRange} class. +
++A print job is represented by the {@link android.print.PrintJob} class which has behavior +methods as well as methods for querying its state. Each print job has a unique id represented +by the {@link android.print.PrintJobId} class and exposes APIs for obtaining a {@link +android.print.PrintJobInfo} which is a snapshot of its state. The print job state may +change over time. +
++An available printer represented by the {@link android.print.PrinterInfo} class has a +unique id which is abstracted by the {@link android.print.PrinterId} class. The {@link +android.print.PrinterInfo} contains printer properties such as id, name, description, status, +and printer capabilities encapsulated in the {@link android.print.PrinterCapabilitiesInfo} +class. Printer capabilities describe how a printer can print content, for example what are +the supported media sizes, color modes, resolutions, etc. +
+ + diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java index fdeb3730b90e1c244928fd18bf3ab5a2a4a0b07d..6fa0bddb43f48e574c37111a67ed39f5e6a1c4bb 100644 --- a/core/java/android/printservice/PrintJob.java +++ b/core/java/android/printservice/PrintJob.java @@ -321,7 +321,7 @@ public final class PrintJob { */ public String getAdvancedStringOption(String key) { PrintService.throwIfNotCalledOnMainThread(); - return null; + return getInfo().getAdvancedStringOption(key); } /** @@ -333,7 +333,7 @@ public final class PrintJob { */ public boolean hasAdvancedOption(String key) { PrintService.throwIfNotCalledOnMainThread(); - return false; + return getInfo().hasAdvancedOption(key); } /** @@ -344,7 +344,7 @@ public final class PrintJob { */ public int getAdvancedIntOption(String key) { PrintService.throwIfNotCalledOnMainThread(); - return 0; + return getInfo().getAdvancedIntOption(key); } @Override diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java index 0fc5f7f035a6a78658e6412af65d871964577a88..eb0ac2e631b8486f2594913aaed97aaf70c4a63c 100644 --- a/core/java/android/printservice/PrintService.java +++ b/core/java/android/printservice/PrintService.java @@ -209,6 +209,14 @@ public abstract class PrintService extends Service { * PrintJob#getAdvancedStringOption(String) PrintJob.getAdvancedStringOption(String)} * and {@link PrintJob#getAdvancedIntOption(String) PrintJob.getAdvancedIntOption(String)}. *
+ *+ * If the advanced print options activity offers changes to the standard print + * options, you can get the current {@link android.print.PrinterInfo} using the + * "android.intent.extra.print.EXTRA_PRINTER_INFO" extra which will allow you to + * present the user with UI options supported by the current printer. For example, + * if the current printer does not support a give media size, you should not + * offer it in the advanced print options dialog. + *
*/ public static final String EXTRA_PRINT_JOB_INFO = "android.intent.extra.print.PRINT_JOB_INFO"; diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java index 8e9636c2b7335016500f5e64b8cfba173e87a7bb..a2c6c09e4932f1c65a766c278a43ed9adaf02a05 100644 --- a/core/java/android/printservice/PrintServiceInfo.java +++ b/core/java/android/printservice/PrintServiceInfo.java @@ -60,6 +60,8 @@ public final class PrintServiceInfo implements Parcelable { private final String mAddPrintersActivityName; + private final String mAdvancedPrintOptionsActivityName; + /** * Creates a new instance. * @@ -70,6 +72,7 @@ public final class PrintServiceInfo implements Parcelable { mResolveInfo = parcel.readParcelable(null); mSettingsActivityName = parcel.readString(); mAddPrintersActivityName = parcel.readString(); + mAdvancedPrintOptionsActivityName = parcel.readString(); } /** @@ -78,14 +81,16 @@ public final class PrintServiceInfo implements Parcelable { * @param resolveInfo The service resolve info. * @param settingsActivityName Optional settings activity name. * @param addPrintersActivityName Optional add printers activity name. + * @param advancedPrintOptionsActivityName Optional advanced print options activity. */ public PrintServiceInfo(ResolveInfo resolveInfo, String settingsActivityName, - String addPrintersActivityName) { + String addPrintersActivityName, String advancedPrintOptionsActivityName) { mId = new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name).flattenToString(); mResolveInfo = resolveInfo; mSettingsActivityName = settingsActivityName; mAddPrintersActivityName = addPrintersActivityName; + mAdvancedPrintOptionsActivityName = advancedPrintOptionsActivityName; } /** @@ -99,6 +104,7 @@ public final class PrintServiceInfo implements Parcelable { public static PrintServiceInfo create(ResolveInfo resolveInfo, Context context) { String settingsActivityName = null; String addPrintersActivityName = null; + String advancedPrintOptionsActivityName = null; XmlResourceParser parser = null; PackageManager packageManager = context.getPackageManager(); @@ -128,6 +134,9 @@ public final class PrintServiceInfo implements Parcelable { addPrintersActivityName = attributes.getString( com.android.internal.R.styleable.PrintService_addPrintersActivity); + advancedPrintOptionsActivityName = attributes.getString(com.android.internal + .R.styleable.PrintService_advancedPrintOptionsActivity); + attributes.recycle(); } } catch (IOException ioe) { @@ -144,7 +153,8 @@ public final class PrintServiceInfo implements Parcelable { } } - return new PrintServiceInfo(resolveInfo, settingsActivityName, addPrintersActivityName); + return new PrintServiceInfo(resolveInfo, settingsActivityName, + addPrintersActivityName, advancedPrintOptionsActivityName); } /** @@ -194,6 +204,19 @@ public final class PrintServiceInfo implements Parcelable { return mAddPrintersActivityName; } + /** + * The advanced print options activity name. + *+ * Statically set from + * {@link PrintService#SERVICE_META_DATA meta-data}. + *
+ * + * @return The advanced print options activity name. + */ + public String getAdvancedOptionsActivityName() { + return mAdvancedPrintOptionsActivityName; + } + /** * {@inheritDoc} */ @@ -206,6 +229,7 @@ public final class PrintServiceInfo implements Parcelable { parcel.writeParcelable(mResolveInfo, 0); parcel.writeString(mSettingsActivityName); parcel.writeString(mAddPrintersActivityName); + parcel.writeString(mAdvancedPrintOptionsActivityName); } @Override @@ -243,6 +267,8 @@ public final class PrintServiceInfo implements Parcelable { builder.append(", resolveInfo=").append(mResolveInfo); builder.append(", settingsActivityName=").append(mSettingsActivityName); builder.append(", addPrintersActivityName=").append(mAddPrintersActivityName); + builder.append(", advancedPrintOptionsActivityName=") + .append(mAdvancedPrintOptionsActivityName); builder.append("}"); return builder.toString(); } diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 7f8dca2f42408a2cbe1f10890e0f7dcdd8c560a9..86d3cf864a42c5556f1e2dc5b56adb0c0f121a29 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -34,6 +34,7 @@ import android.media.ExifInterface; import android.net.Uri; import android.os.Bundle; import android.os.CancellationSignal; +import android.os.OperationCanceledException; import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor.OnCloseListener; import android.os.RemoteException; @@ -81,6 +82,9 @@ public final class DocumentsContract { /** {@hide} */ public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME"; + /** {@hide} */ + public static final String EXTRA_SHOW_ADVANCED = "android.content.extra.SHOW_ADVANCED"; + /** * Included in {@link AssetFileDescriptor#getExtras()} when returned * thumbnail should be rotated. @@ -667,7 +671,9 @@ public final class DocumentsContract { try { return getDocumentThumbnail(client, documentUri, size, signal); } catch (Exception e) { - Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e); + if (!(e instanceof OperationCanceledException)) { + Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e); + } return null; } finally { ContentProviderClient.releaseQuietly(client); diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index c9efb53a959d9b9652e30107ce3c4b74b4516c6f..49816f8091b6da19547dfcfc6b80fad45d8a09aa 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -62,7 +62,8 @@ import java.io.FileNotFoundException; * android:authorities="com.example.mycloudprovider" * android:exported="true" * android:grantUriPermissions="true" - * android:permission="android.permission.MANAGE_DOCUMENTS"> + * android:permission="android.permission.MANAGE_DOCUMENTS" + * android:enabled="@bool/isAtLeastKitKat"> * <intent-filter> * <action android:name="android.content.action.DOCUMENTS_PROVIDER" /> * </intent-filter> @@ -76,9 +77,8 @@ import java.io.FileNotFoundException; * only the system can obtain. Applications cannot use a documents provider * directly; they must go through {@link Intent#ACTION_OPEN_DOCUMENT} or * {@link Intent#ACTION_CREATE_DOCUMENT} which requires a user to actively - * navigate and select documents. When a user selects documents through that - * UI, the system issues narrow URI permission grants to the requesting - * application. + * navigate and select documents. When a user selects documents through that UI, + * the system issues narrow URI permission grants to the requesting application. * *@@ -91,8 +91,8 @@ import java.io.FileNotFoundException; *
* Each document can have different capabilities, as described by * {@link Document#COLUMN_FLAGS}. For example, if a document can be represented - * as a thumbnail, a provider can set {@link Document#FLAG_SUPPORTS_THUMBNAIL} - * and implement + * as a thumbnail, your provider can set + * {@link Document#FLAG_SUPPORTS_THUMBNAIL} and implement * {@link #openDocumentThumbnail(String, Point, CancellationSignal)} to return * that thumbnail. *
@@ -102,7 +102,7 @@ import java.io.FileNotFoundException; * single document can be included in multiple directories when responding to * {@link #queryChildDocuments(String, String[], String)}. For example, a * provider might surface a single photo in multiple locations: once in a - * directory of locations, and again in a directory of dates. + * directory of geographic locations, and again in a directory of dates. * *@@ -162,7 +162,7 @@ public abstract class DocumentsProvider extends ContentProvider { /** * Create a new document and return its newly generated - * {@link Document#COLUMN_DOCUMENT_ID}. A provider must allocate a new + * {@link Document#COLUMN_DOCUMENT_ID}. You must allocate a new * {@link Document#COLUMN_DOCUMENT_ID} to represent the document, which must * not change once returned. * @@ -194,16 +194,17 @@ public abstract class DocumentsProvider extends ContentProvider { } /** - * Return all roots currently provided. A provider must define at least one - * root to display to users, and it should avoid making network requests to - * keep this request fast. + * Return all roots currently provided. To display to users, you must define + * at least one root. You should avoid making network requests to keep this + * request fast. *
* Each root is defined by the metadata columns described in {@link Root}, * including {@link Root#COLUMN_DOCUMENT_ID} which points to a directory * representing a tree of documents to display under that root. *
* If this set of roots changes, you must call {@link ContentResolver#notifyChange(Uri, - * android.database.ContentObserver)} to notify the system. + * android.database.ContentObserver, boolean)} with + * {@link DocumentsContract#buildRootsUri(String)} to notify the system. * * @param projection list of {@link Root} columns to put into the cursor. If * {@code null} all supported columns should be included. @@ -216,6 +217,8 @@ public abstract class DocumentsProvider extends ContentProvider { * {@link Root#FLAG_SUPPORTS_RECENTS}. The returned documents should be * sorted by {@link Document#COLUMN_LAST_MODIFIED} in descending order, and * limited to only return the 64 most recently modified documents. + *
+ * Recent documents do not support change notifications. * * @param projection list of {@link Document} columns to put into the * cursor. If {@code null} all supported columns should be @@ -229,8 +232,8 @@ public abstract class DocumentsProvider extends ContentProvider { } /** - * Return metadata for the single requested document. A provider should - * avoid making network requests to keep this request fast. + * Return metadata for the single requested document. You should avoid + * making network requests to keep this request fast. * * @param documentId the document to return. * @param projection list of {@link Document} columns to put into the @@ -248,10 +251,18 @@ public abstract class DocumentsProvider extends ContentProvider { * If your provider is cloud-based, and you have some data cached or pinned * locally, you may return the local data immediately, setting * {@link DocumentsContract#EXTRA_LOADING} on the Cursor to indicate that - * your provider is still fetching additional data. Then, when the network - * data is available, you can call {@link ContentResolver#notifyChange(Uri, - * android.database.ContentObserver)} to trigger a requery and return the - * complete contents. + * you are still fetching additional data. Then, when the network data is + * available, you can send a change notification to trigger a requery and + * return the complete contents. To return a Cursor with extras, you need to + * extend and override {@link Cursor#getExtras()}. + *
+ * To support change notifications, you must + * {@link Cursor#setNotificationUri(ContentResolver, Uri)} with a relevant + * Uri, such as + * {@link DocumentsContract#buildChildDocumentsUri(String, String)}. Then + * you can call {@link ContentResolver#notifyChange(Uri, + * android.database.ContentObserver, boolean)} with that Uri to send change + * notifications. * * @param parentDocumentId the directory to return children for. * @param projection list of {@link Document} columns to put into the @@ -289,6 +300,20 @@ public abstract class DocumentsProvider extends ContentProvider { *
* Only documents may be returned; directories are not supported in search * results. + *
+ * If your provider is cloud-based, and you have some data cached or pinned + * locally, you may return the local data immediately, setting + * {@link DocumentsContract#EXTRA_LOADING} on the Cursor to indicate that + * you are still fetching additional data. Then, when the network data is + * available, you can send a change notification to trigger a requery and + * return the complete contents. + *
+ * To support change notifications, you must + * {@link Cursor#setNotificationUri(ContentResolver, Uri)} with a relevant + * Uri, such as {@link DocumentsContract#buildSearchDocumentsUri(String, + * String, String)}. Then you can call {@link ContentResolver#notifyChange(Uri, + * android.database.ContentObserver, boolean)} with that Uri to send change + * notifications. * * @param rootId the root to search under. * @param query string to match documents against. @@ -327,22 +352,19 @@ public abstract class DocumentsProvider extends ContentProvider { /** * Open and return the requested document. *
- * A provider should return a reliable {@link ParcelFileDescriptor} to + * Your provider should return a reliable {@link ParcelFileDescriptor} to * detect when the remote caller has finished reading or writing the - * document. A provider may return a pipe or socket pair if the mode is - * exclusively {@link ParcelFileDescriptor#MODE_READ_ONLY} or - * {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, but complex modes like - * {@link ParcelFileDescriptor#MODE_READ_WRITE} require a normal file on - * disk. + * document. You may return a pipe or socket pair if the mode is exclusively + * "r" or "w", but complex modes like "rw" imply a normal file on disk that + * supports seeking. *
- * If a provider blocks while downloading content, it should periodically - * check {@link CancellationSignal#isCanceled()} to abort abandoned open - * requests. + * If you block while downloading content, you should periodically check + * {@link CancellationSignal#isCanceled()} to abort abandoned open requests. * * @param documentId the document to return. * @param mode the mode to open with, such as 'r', 'w', or 'rw'. * @param signal used by the caller to signal if the request should be - * cancelled. + * cancelled. May be null. * @see ParcelFileDescriptor#open(java.io.File, int, android.os.Handler, * OnCloseListener) * @see ParcelFileDescriptor#createReliablePipe() @@ -359,15 +381,14 @@ public abstract class DocumentsProvider extends ContentProvider { * attempting to serve from a local cache if possible. A provider should * never return images more than double the hinted size. *
- * If a provider performs expensive operations to download or generate a
- * thumbnail, it should periodically check
- * {@link CancellationSignal#isCanceled()} to abort abandoned thumbnail
- * requests.
+ * If you perform expensive operations to download or generate a thumbnail,
+ * you should periodically check {@link CancellationSignal#isCanceled()} to
+ * abort abandoned thumbnail requests.
*
* @param documentId the document to return.
* @param sizeHint hint of the optimal thumbnail dimensions.
* @param signal used by the caller to signal if the request should be
- * cancelled.
+ * cancelled. May be null.
* @see Document#FLAG_SUPPORTS_THUMBNAIL
*/
@SuppressWarnings("unused")
@@ -495,10 +516,7 @@ public abstract class DocumentsProvider extends ContentProvider {
final boolean callerHasManage =
context.checkCallingOrSelfPermission(android.Manifest.permission.MANAGE_DOCUMENTS)
== PackageManager.PERMISSION_GRANTED;
- if (!callerHasManage) {
- getContext().enforceCallingOrSelfUriPermission(
- documentUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, method);
- }
+ enforceWritePermissionInner(documentUri);
final Bundle out = new Bundle();
try {
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index b8083632bc4fcba716c9ec88b721180fce86ce35..2752085a427e06c6849088f9b29f87610098bb3d 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -993,8 +993,16 @@ public class TextToSpeech {
return runAction(new Action IME authors: If the class is not one you
* understand, assume {@link #TYPE_CLASS_TEXT} with NO variation
- * or flags.
+ * or flags.
*/
public static final int TYPE_MASK_CLASS = 0x0000000f;
@@ -69,7 +69,10 @@ public interface InputType {
* This should be interpreted to mean that the target input connection
* is not rich, it can not process and show things like candidate text nor
* retrieve the current text, so the input method will need to run in a
- * limited "generate key events" mode.
+ * limited "generate key events" mode, if it supports it. Note that some
+ * input methods may not support it, for example a voice-based input
+ * method will likely not be able to generate key events even if this
+ * flag is set.
*/
public static final int TYPE_NULL = 0x00000000;
@@ -94,48 +97,70 @@ public interface InputType {
* Flag for {@link #TYPE_CLASS_TEXT}: capitalize all characters. Overrides
* {@link #TYPE_TEXT_FLAG_CAP_WORDS} and
* {@link #TYPE_TEXT_FLAG_CAP_SENTENCES}. This value is explicitly defined
- * to be the same as {@link TextUtils#CAP_MODE_CHARACTERS}.
+ * to be the same as {@link TextUtils#CAP_MODE_CHARACTERS}. Of course,
+ * this only affects languages where there are upper-case and lower-case letters.
*/
public static final int TYPE_TEXT_FLAG_CAP_CHARACTERS = 0x00001000;
/**
- * Flag for {@link #TYPE_CLASS_TEXT}: capitalize first character of
- * all words. Overrides {@link #TYPE_TEXT_FLAG_CAP_SENTENCES}. This
+ * Flag for {@link #TYPE_CLASS_TEXT}: capitalize the first character of
+ * every word. Overrides {@link #TYPE_TEXT_FLAG_CAP_SENTENCES}. This
* value is explicitly defined
- * to be the same as {@link TextUtils#CAP_MODE_WORDS}.
+ * to be the same as {@link TextUtils#CAP_MODE_WORDS}. Of course,
+ * this only affects languages where there are upper-case and lower-case letters.
*/
public static final int TYPE_TEXT_FLAG_CAP_WORDS = 0x00002000;
/**
- * Flag for {@link #TYPE_CLASS_TEXT}: capitalize first character of
+ * Flag for {@link #TYPE_CLASS_TEXT}: capitalize the first character of
* each sentence. This value is explicitly defined
- * to be the same as {@link TextUtils#CAP_MODE_SENTENCES}.
+ * to be the same as {@link TextUtils#CAP_MODE_SENTENCES}. For example
+ * in English it means to capitalize after a period and a space (note that other
+ * languages may have different characters for period, or not use spaces,
+ * or use different grammatical rules). Of course,
+ * this only affects languages where there are upper-case and lower-case letters.
*/
public static final int TYPE_TEXT_FLAG_CAP_SENTENCES = 0x00004000;
/**
* Flag for {@link #TYPE_CLASS_TEXT}: the user is entering free-form
- * text that should have auto-correction applied to it.
+ * text that should have auto-correction applied to it. Without this flag,
+ * the IME will not try to correct typos. You should always set this flag
+ * unless you really expect users to type non-words in this field, for
+ * example to choose a name for a character in a game.
+ * Contrast this with {@link #TYPE_TEXT_FLAG_AUTO_COMPLETE} and
+ * {@link #TYPE_TEXT_FLAG_NO_SUGGESTIONS}:
+ * {@code TYPE_TEXT_FLAG_AUTO_CORRECT} means that the IME will try to
+ * auto-correct typos as the user is typing, but does not define whether
+ * the IME offers an interface to show suggestions.
*/
public static final int TYPE_TEXT_FLAG_AUTO_CORRECT = 0x00008000;
/**
- * Flag for {@link #TYPE_CLASS_TEXT}: the text editor is performing
- * auto-completion of the text being entered based on its own semantics,
- * which it will present to the user as they type. This generally means
- * that the input method should not be showing candidates itself, but can
- * expect for the editor to supply its own completions/candidates from
+ * Flag for {@link #TYPE_CLASS_TEXT}: the text editor (which means
+ * the application) is performing auto-completion of the text being entered
+ * based on its own semantics, which it will present to the user as they type.
+ * This generally means that the input method should not be showing
+ * candidates itself, but can expect the editor to supply its own
+ * completions/candidates from
* {@link android.view.inputmethod.InputMethodSession#displayCompletions
* InputMethodSession.displayCompletions()} as a result of the editor calling
* {@link android.view.inputmethod.InputMethodManager#displayCompletions
* InputMethodManager.displayCompletions()}.
+ * Note the contrast with {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} and
+ * {@link #TYPE_TEXT_FLAG_NO_SUGGESTIONS}:
+ * {@code TYPE_TEXT_FLAG_AUTO_COMPLETE} means the editor should show an
+ * interface for displaying suggestions, but instead of supplying its own
+ * it will rely on the Editor to pass completions/corrections.
*/
public static final int TYPE_TEXT_FLAG_AUTO_COMPLETE = 0x00010000;
/**
* Flag for {@link #TYPE_CLASS_TEXT}: multiple lines of text can be
* entered into the field. If this flag is not set, the text field
- * will be constrained to a single line.
+ * will be constrained to a single line. The IME may also choose not to
+ * display an enter key when this flag is not set, as there should be no
+ * need to create new lines.
*/
public static final int TYPE_TEXT_FLAG_MULTI_LINE = 0x00020000;
@@ -152,6 +177,16 @@ public interface InputType {
* do not contain words from the language and do not benefit from any
* dictionary-based completions or corrections. It overrides the
* {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} value when set.
+ * Please avoid using this unless you are certain this is what you want.
+ * Many input methods need suggestions to work well, for example the ones
+ * based on gesture typing. Consider clearing
+ * {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} instead if you just do not
+ * want the IME to correct typos.
+ * Note the contrast with {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} and
+ * {@link #TYPE_TEXT_FLAG_AUTO_COMPLETE}:
+ * {@code TYPE_TEXT_FLAG_NO_SUGGESTIONS} means the IME should never
+ * show an interface to display suggestions. Most IMEs will also take this to
+ * mean they should not try to auto-correct what the user is typing.
*/
public static final int TYPE_TEXT_FLAG_NO_SUGGESTIONS = 0x00080000;
@@ -224,7 +259,9 @@ public interface InputType {
/**
* Variation of {@link #TYPE_CLASS_TEXT}: entering text for phonetic
- * pronunciation, such as a phonetic name field in contacts.
+ * pronunciation, such as a phonetic name field in contacts. This is mostly
+ * useful for languages where one spelling may have several phonetic
+ * readings, like Japanese.
*/
public static final int TYPE_TEXT_VARIATION_PHONETIC = 0x000000c0;
@@ -255,12 +292,13 @@ public interface InputType {
// ----------------------------------------------------------------------
/**
- * Class for numeric text. This class supports the following flag:
+ * Class for numeric text. This class supports the following flags:
* {@link #TYPE_NUMBER_FLAG_SIGNED} and
* {@link #TYPE_NUMBER_FLAG_DECIMAL}. It also supports the following
* variations: {@link #TYPE_NUMBER_VARIATION_NORMAL} and
- * {@link #TYPE_NUMBER_VARIATION_PASSWORD}. If you do not recognize
- * the variation, normal should be assumed.
+ * {@link #TYPE_NUMBER_VARIATION_PASSWORD}.
+ * IME authors: If you do not recognize
+ * the variation, normal should be assumed.
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 354ea6613a397fd861c310ff1e3f0a415d7c43c5..7d310a254990888e684e133d0871eaea832de836 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -643,6 +643,15 @@ public final class Display {
|| uid == 0;
}
+ /**
+ * Returns true if the display is a public presentation display.
+ * @hide
+ */
+ public boolean isPublicPresentation() {
+ return (mFlags & (Display.FLAG_PRIVATE | Display.FLAG_PRESENTATION)) ==
+ Display.FLAG_PRESENTATION;
+ }
+
private void updateDisplayInfoLocked() {
// Note: The display manager caches display info objects on our behalf.
DisplayInfo newInfo = mGlobal.getDisplayInfo(mDisplayId);
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 9d4af006258e2ea448fe995eed27df7cd65f4458..c92a104b239d4dd553a21c5c1981e16ed730d90d 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -78,7 +78,8 @@ interface IWindowManager
void addWindowToken(IBinder token, int type);
void removeWindowToken(IBinder token);
void addAppToken(int addPos, IApplicationToken token, int groupId, int stackId,
- int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId);
+ int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
+ int configChanges);
void setAppGroupId(IBinder token, int groupId);
void setAppOrientation(IApplicationToken token, int requestedOrientation);
int getAppOrientation(IApplicationToken token);
@@ -211,7 +212,7 @@ interface IWindowManager
/**
* Called by the status bar to notify Views of changes to System UI visiblity.
*/
- void statusBarVisibilityChanged(int visibility);
+ oneway void statusBarVisibilityChanged(int visibility);
/**
* Block until the given window has been drawn to the screen.
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index f5ee7edca50ae76e8b26059870a1c1d43893b73e..25972e7737a85fd35c6929764a19b23b7c58cf82 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -48,7 +48,7 @@ public abstract class InputEventReceiver {
InputChannel inputChannel, MessageQueue messageQueue);
private static native void nativeDispose(int receiverPtr);
private static native void nativeFinishInputEvent(int receiverPtr, int seq, boolean handled);
- private static native void nativeConsumeBatchedInputEvents(int receiverPtr,
+ private static native boolean nativeConsumeBatchedInputEvents(int receiverPtr,
long frameTimeNanos);
/**
@@ -165,14 +165,17 @@ public abstract class InputEventReceiver {
*
* @param frameTimeNanos The time in the {@link System#nanoTime()} time base
* when the current display frame started rendering, or -1 if unknown.
+ *
+ * @return Whether a batch was consumed
*/
- public final void consumeBatchedInputEvents(long frameTimeNanos) {
+ public final boolean consumeBatchedInputEvents(long frameTimeNanos) {
if (mReceiverPtr == 0) {
Log.w(TAG, "Attempted to consume batched input events but the input event "
+ "receiver has already been disposed.");
} else {
- nativeConsumeBatchedInputEvents(mReceiverPtr, frameTimeNanos);
+ return nativeConsumeBatchedInputEvents(mReceiverPtr, frameTimeNanos);
}
+ return false;
}
// Called from native code.
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index f36c78fd480783f111a1051a8b4f3fbe9dd13b35..42a58a8efc22239cbea53a24c29090e503ea1a4b 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -323,6 +323,10 @@ public class ScaleGestureDetector {
mInProgress = false;
mInitialSpan = 0;
mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
+ } else if (mDoubleTapMode == DOUBLE_TAP_MODE_IN_PROGRESS && streamComplete) {
+ mInProgress = false;
+ mInitialSpan = 0;
+ mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
}
if (streamComplete) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e2d98edafc49afde5477b711c655165deee24324..06f8e8c3607aff85080ca3c8eeb71be970449be5 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -18,7 +18,6 @@ package android.view;
import android.content.ClipData;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -3102,6 +3101,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
private static final int UNDEFINED_PADDING = Integer.MIN_VALUE;
+ /**
+ * Cache if a left padding has been defined
+ */
+ private boolean mLeftPaddingDefined = false;
+
+ /**
+ * Cache if a right padding has been defined
+ */
+ private boolean mRightPaddingDefined = false;
+
/**
* @hide
*/
@@ -3530,10 +3539,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
int overScrollMode = mOverScrollMode;
boolean initializeScrollbars = false;
- boolean leftPaddingDefined = false;
- boolean rightPaddingDefined = false;
boolean startPaddingDefined = false;
boolean endPaddingDefined = false;
+ boolean leftPaddingDefined = false;
+ boolean rightPaddingDefined = false;
final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
@@ -3865,6 +3874,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
setBackground(background);
}
+ // setBackground above will record that padding is currently provided by the background.
+ // If we have padding specified via xml, record that here instead and use it.
+ mLeftPaddingDefined = leftPaddingDefined;
+ mRightPaddingDefined = rightPaddingDefined;
+
if (padding >= 0) {
leftPadding = padding;
topPadding = padding;
@@ -3882,11 +3896,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// Padding from the background drawable is stored at this point in mUserPaddingLeftInitial
// and mUserPaddingRightInitial) so drawable padding will be used as ultimate default if
// defined.
- if (!leftPaddingDefined && startPaddingDefined) {
+ if (!mLeftPaddingDefined && startPaddingDefined) {
leftPadding = startPadding;
}
mUserPaddingLeftInitial = (leftPadding >= 0) ? leftPadding : mUserPaddingLeftInitial;
- if (!rightPaddingDefined && endPaddingDefined) {
+ if (!mRightPaddingDefined && endPaddingDefined) {
rightPadding = endPadding;
}
mUserPaddingRightInitial = (rightPadding >= 0) ? rightPadding : mUserPaddingRightInitial;
@@ -3898,10 +3912,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// defined.
final boolean hasRelativePadding = startPaddingDefined || endPaddingDefined;
- if (leftPaddingDefined && !hasRelativePadding) {
+ if (mLeftPaddingDefined && !hasRelativePadding) {
mUserPaddingLeftInitial = leftPadding;
}
- if (rightPaddingDefined && !hasRelativePadding) {
+ if (mRightPaddingDefined && !hasRelativePadding) {
mUserPaddingRightInitial = rightPadding;
}
}
@@ -5900,6 +5914,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
sThreadLocal.set(localInsets);
}
boolean res = computeFitSystemWindows(insets, localInsets);
+ mUserPaddingLeftInitial = localInsets.left;
+ mUserPaddingRightInitial = localInsets.right;
internalSetPadding(localInsets.left, localInsets.top,
localInsets.right, localInsets.bottom);
return res;
@@ -12133,12 +12149,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (!isTextAlignmentResolved()) {
resolveTextAlignment();
}
- if (!isPaddingResolved()) {
- resolvePadding();
- }
+ // Should resolve Drawables before Padding because we need the layout direction of the
+ // Drawable to correctly resolve Padding.
if (!isDrawablesResolved()) {
resolveDrawables();
}
+ if (!isPaddingResolved()) {
+ resolvePadding();
+ }
onRtlPropertiesChanged(getLayoutDirection());
return true;
}
@@ -12341,6 +12359,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// If start / end padding are defined, they will be resolved (hence overriding) to
// left / right or right / left depending on the resolved layout direction.
// If start / end padding are not defined, use the left / right ones.
+ if (mBackground != null && (!mLeftPaddingDefined || !mRightPaddingDefined)) {
+ Rect padding = sThreadLocal.get();
+ if (padding == null) {
+ padding = new Rect();
+ sThreadLocal.set(padding);
+ }
+ mBackground.getPadding(padding);
+ if (!mLeftPaddingDefined) {
+ mUserPaddingLeftInitial = padding.left;
+ }
+ if (!mRightPaddingDefined) {
+ mUserPaddingRightInitial = padding.right;
+ }
+ }
switch (resolvedLayoutDirection) {
case LAYOUT_DIRECTION_RTL:
if (mUserPaddingStart != UNDEFINED_PADDING) {
@@ -15336,6 +15368,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mUserPaddingRightInitial = padding.right;
internalSetPadding(padding.left, padding.top, padding.right, padding.bottom);
}
+ mLeftPaddingDefined = false;
+ mRightPaddingDefined = false;
}
// Compare the minimum sizes of the old Drawable and the new. If there isn't an old or
@@ -15432,6 +15466,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mUserPaddingLeftInitial = left;
mUserPaddingRightInitial = right;
+ mLeftPaddingDefined = true;
+ mRightPaddingDefined = true;
+
internalSetPadding(left, top, right, bottom);
}
@@ -15517,6 +15554,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mUserPaddingStart = start;
mUserPaddingEnd = end;
+ mLeftPaddingDefined = true;
+ mRightPaddingDefined = true;
switch(getLayoutDirection()) {
case LAYOUT_DIRECTION_RTL:
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 946106868d57cfa88ce80758c1021270bbf2e221..bc0d7e3c806be0a0f040e67017d0afc8b41419c6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -77,8 +77,10 @@ import com.android.internal.policy.PolicyManager;
import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.RootViewSurfaceTaker;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.OutputStream;
+import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
@@ -3391,16 +3393,7 @@ public final class ViewRootImpl implements ViewParent,
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);
- } else if (mView == null || !mAdded) {
- Slog.w(TAG, "Dropping event due to root view being removed: " + q.mEvent);
- finish(q, false);
- } else if (!mAttachInfo.mHasWindowFocus &&
- !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) &&
- !isTerminalInputEvent(q.mEvent)) {
- // If this is a focused event and the window doesn't currently have input focus,
- // then drop this event. This could be an event that came back from the previous
- // stage but the window has lost focus in the meantime.
- Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
+ } else if (shouldDropInputEvent(q)) {
finish(q, false);
} else {
apply(q, onProcess(q));
@@ -3458,6 +3451,28 @@ public final class ViewRootImpl implements ViewParent,
finishInputEvent(q);
}
}
+
+ protected boolean shouldDropInputEvent(QueuedInputEvent q) {
+ if (mView == null || !mAdded) {
+ Slog.w(TAG, "Dropping event due to root view being removed: " + q.mEvent);
+ return true;
+ } else if (!mAttachInfo.mHasWindowFocus &&
+ !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) &&
+ !isTerminalInputEvent(q.mEvent)) {
+ // If this is a focused event and the window doesn't currently have input focus,
+ // then drop this event. This could be an event that came back from the previous
+ // stage but the window has lost focus in the meantime.
+ Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
+ return true;
+ }
+ return false;
+ }
+
+ void dump(String prefix, PrintWriter writer) {
+ if (mNext != null) {
+ mNext.dump(prefix, writer);
+ }
+ }
}
/**
@@ -3595,6 +3610,16 @@ public final class ViewRootImpl implements ViewParent,
mQueueLength -= 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength);
}
+
+ @Override
+ void dump(String prefix, PrintWriter writer) {
+ writer.print(prefix);
+ writer.print(getClass().getName());
+ writer.print(": mQueueLength=");
+ writer.println(mQueueLength);
+
+ super.dump(prefix, writer);
+ }
}
/**
@@ -3828,6 +3853,10 @@ public final class ViewRootImpl implements ViewParent,
return FINISH_HANDLED;
}
+ if (shouldDropInputEvent(q)) {
+ return FINISH_NOT_HANDLED;
+ }
+
// If the Control modifier is held, try to interpret the key as a shortcut.
if (event.getAction() == KeyEvent.ACTION_DOWN
&& event.isCtrlPressed()
@@ -3836,12 +3865,18 @@ public final class ViewRootImpl implements ViewParent,
if (mView.dispatchKeyShortcutEvent(event)) {
return FINISH_HANDLED;
}
+ if (shouldDropInputEvent(q)) {
+ return FINISH_NOT_HANDLED;
+ }
}
// Apply the fallback event policy.
if (mFallbackEventHandler.dispatchKeyEvent(event)) {
return FINISH_HANDLED;
}
+ if (shouldDropInputEvent(q)) {
+ return FINISH_NOT_HANDLED;
+ }
// Handle automatic focus changes.
if (event.getAction() == KeyEvent.ACTION_DOWN) {
@@ -5201,6 +5236,53 @@ public final class ViewRootImpl implements ViewParent,
mView.debug();
}
+ public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ String innerPrefix = prefix + " ";
+ writer.print(prefix); writer.println("ViewRoot:");
+ writer.print(innerPrefix); writer.print("mAdded="); writer.print(mAdded);
+ writer.print(" mRemoved="); writer.println(mRemoved);
+ writer.print(innerPrefix); writer.print("mConsumeBatchedInputScheduled=");
+ writer.println(mConsumeBatchedInputScheduled);
+ writer.print(innerPrefix); writer.print("mPendingInputEventCount=");
+ writer.println(mPendingInputEventCount);
+ writer.print(innerPrefix); writer.print("mProcessInputEventsScheduled=");
+ writer.println(mProcessInputEventsScheduled);
+ writer.print(innerPrefix); writer.print("mTraversalScheduled=");
+ writer.print(mTraversalScheduled);
+ if (mTraversalScheduled) {
+ writer.print(" (barrier="); writer.print(mTraversalBarrier); writer.println(")");
+ } else {
+ writer.println();
+ }
+ mFirstInputStage.dump(innerPrefix, writer);
+
+ mChoreographer.dump(prefix, writer);
+
+ writer.print(prefix); writer.println("View Hierarchy:");
+ dumpViewHierarchy(innerPrefix, writer, mView);
+ }
+
+ private void dumpViewHierarchy(String prefix, PrintWriter writer, View view) {
+ writer.print(prefix);
+ if (view == null) {
+ writer.println("null");
+ return;
+ }
+ writer.println(view.toString());
+ if (!(view instanceof ViewGroup)) {
+ return;
+ }
+ ViewGroup grp = (ViewGroup)view;
+ final int N = grp.getChildCount();
+ if (N <= 0) {
+ return;
+ }
+ prefix = prefix + " ";
+ for (int i=0; ilayoutId
parameter. If such a Scene has already been created,
- * that same Scene will be returned. This caching of layoutId-based scenes enables
- * sharing of common scenes between those created in code and those referenced
- * by {@link TransitionManager} XML resource files.
+ * layoutId
parameter. If such a Scene has already been created for
+ * the given sceneRoot
, that same Scene will be returned.
+ * This caching of layoutId-based scenes enables sharing of common scenes
+ * between those created in code and those referenced by {@link TransitionManager}
+ * XML resource files.
*
* @param sceneRoot The root of the hierarchy in which scene changes
* and transitions will take place.
* @param layoutId The id of a standard layout resource file.
* @param context The context used in the process of inflating
* the layout resource.
- * @return
+ * @return The scene for the given root and layout id
*/
public static Scene getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context) {
- SparseArray
It should be defined in an XML resource file with an {@code <input-method>} element. + * For more information, see the guide to + * + * Creating an Input Method.
+ * + * @see InputMethodSubtype + * + * @attr ref android.R.styleable#InputMethod_settingsActivity + * @attr ref android.R.styleable#InputMethod_isDefault + * @attr ref android.R.styleable#InputMethod_supportsSwitchingToNextInputMethod */ public final class InputMethodInfo implements Parcelable { static final String TAG = "InputMethodInfo"; diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java index 88b29776fa0f6a89f04f9dac9fd5019901e26845..2ab30246662caf1acd903dc5912bc6b759aff5c1 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtype.java +++ b/core/java/android/view/inputmethod/InputMethodSubtype.java @@ -38,9 +38,22 @@ import java.util.Locale; * the specified subtype of the designated IME directly. * *It should be defined in an XML resource file of the input method with the
- * <subtype>
element. For more information, see the guide to
- *
+ * <subtype>
element, which resides within an {@code <input-method>} element.
+ * For more information, see the guide to
+ *
* Creating an Input Method.
+ * + * Note: VideoView does not retain its full state when going into the + * background. In particular, it does not restore the current play state, + * play position, selected tracks, or any subtitle tracks added via + * {@link #addSubtitleSource addSubtitleSource()}. Applications should + * save and restore these on their own in + * {@link android.app.Activity#onSaveInstanceState} and + * {@link android.app.Activity#onRestoreInstanceState}.
+ * Also note that the audio session id (from {@link #getAudioSessionId}) may + * change from its previously returned value when the VideoView is restored. */ public class VideoView extends SurfaceView implements MediaPlayerControl, SubtitleController.Anchor { diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 525517c00eb5068feb8ceffdc55969559b25d50b..43c4b49ce946a44c4d39be76d8db3ee78acac170 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -68,6 +68,8 @@ interface IBatteryStats { void noteFullWifiLockReleasedFromSource(in WorkSource ws); void noteWifiScanStartedFromSource(in WorkSource ws); void noteWifiScanStoppedFromSource(in WorkSource ws); + void noteWifiBatchedScanStartedFromSource(in WorkSource ws, int csph); + void noteWifiBatchedScanStoppedFromSource(in WorkSource ws); void noteWifiMulticastEnabledFromSource(in WorkSource ws); void noteWifiMulticastDisabledFromSource(in WorkSource ws); void noteNetworkInterfaceType(String iface, int type); diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialog.java b/core/java/com/android/internal/app/MediaRouteChooserDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..47d2a9c3468d67a7e3172b7a24a304bc7fc338f9 --- /dev/null +++ b/core/java/com/android/internal/app/MediaRouteChooserDialog.java @@ -0,0 +1,273 @@ +/* + * 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.app; + +import com.android.internal.R; + +import android.app.Dialog; +import android.content.Context; +import android.media.MediaRouter; +import android.media.MediaRouter.RouteInfo; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.ListView; +import android.widget.TextView; + +import java.util.Comparator; + +/** + * This class implements the route chooser dialog for {@link MediaRouter}. + *
+ * This dialog allows the user to choose a route that matches a given selector. + *
+ * + * @see MediaRouteButton + * @see MediaRouteActionProvider + * + * TODO: Move this back into the API, as in the support library media router. + */ +public class MediaRouteChooserDialog extends Dialog { + private final MediaRouter mRouter; + private final MediaRouterCallback mCallback; + + private int mRouteTypes; + private View.OnClickListener mExtendedSettingsClickListener; + private RouteAdapter mAdapter; + private ListView mListView; + private Button mExtendedSettingsButton; + private boolean mAttachedToWindow; + + public MediaRouteChooserDialog(Context context, int theme) { + super(context, theme); + + mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE); + mCallback = new MediaRouterCallback(); + } + + /** + * Gets the media route types for filtering the routes that the user can + * select using the media route chooser dialog. + * + * @return The route types. + */ + public int getRouteTypes() { + return mRouteTypes; + } + + /** + * Sets the types of routes that will be shown in the media route chooser dialog + * launched by this button. + * + * @param types The route types to match. + */ + public void setRouteTypes(int types) { + if (mRouteTypes != types) { + mRouteTypes = types; + + if (mAttachedToWindow) { + mRouter.removeCallback(mCallback); + mRouter.addCallback(types, mCallback, + MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); + } + + refreshRoutes(); + } + } + + public void setExtendedSettingsClickListener(View.OnClickListener listener) { + if (listener != mExtendedSettingsClickListener) { + mExtendedSettingsClickListener = listener; + updateExtendedSettingsButton(); + } + } + + /** + * Returns true if the route should be included in the list. + *+ * The default implementation returns true for enabled non-default routes that + * match the route types. Subclasses can override this method to filter routes + * differently. + *
+ * + * @param route The route to consider, never null. + * @return True if the route should be included in the chooser dialog. + */ + public boolean onFilterRoute(MediaRouter.RouteInfo route) { + return !route.isDefault() && route.isEnabled() && route.matchesTypes(mRouteTypes); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getWindow().requestFeature(Window.FEATURE_LEFT_ICON); + + setContentView(R.layout.media_route_chooser_dialog); + setTitle(mRouteTypes == MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY + ? R.string.media_route_chooser_title_for_remote_display + : R.string.media_route_chooser_title); + + // Must be called after setContentView. + getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, + R.drawable.ic_media_route_off_holo_dark); + + mAdapter = new RouteAdapter(getContext()); + mListView = (ListView)findViewById(R.id.media_route_list); + mListView.setAdapter(mAdapter); + mListView.setOnItemClickListener(mAdapter); + mListView.setEmptyView(findViewById(android.R.id.empty)); + + mExtendedSettingsButton = (Button)findViewById(R.id.media_route_extended_settings_button); + updateExtendedSettingsButton(); + } + + private void updateExtendedSettingsButton() { + if (mExtendedSettingsButton != null) { + mExtendedSettingsButton.setOnClickListener(mExtendedSettingsClickListener); + mExtendedSettingsButton.setVisibility( + mExtendedSettingsClickListener != null ? View.VISIBLE : View.GONE); + } + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + + mAttachedToWindow = true; + mRouter.addCallback(mRouteTypes, mCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); + refreshRoutes(); + } + + @Override + public void onDetachedFromWindow() { + mAttachedToWindow = false; + mRouter.removeCallback(mCallback); + + super.onDetachedFromWindow(); + } + + /** + * Refreshes the list of routes that are shown in the chooser dialog. + */ + public void refreshRoutes() { + if (mAttachedToWindow) { + mAdapter.update(); + } + } + + private final class RouteAdapter extends ArrayAdapter+ * Creates a {@link MediaRouteChooserDialog}. The application may subclass + * this dialog fragment to customize the media route chooser dialog. + *
* - * @see MediaRouteButton - * @see MediaRouteActionProvider + * TODO: Move this back into the API, as in the support library media router. */ public class MediaRouteChooserDialogFragment extends DialogFragment { - private static final String TAG = "MediaRouteChooserDialogFragment"; - public static final String FRAGMENT_TAG = "android:MediaRouteChooserDialogFragment"; - - private static final int[] ITEM_LAYOUTS = new int[] { - R.layout.media_route_list_item_top_header, - R.layout.media_route_list_item_section_header, - R.layout.media_route_list_item, - R.layout.media_route_list_item_checkable, - R.layout.media_route_list_item_collapse_group - }; + private final String ARGUMENT_ROUTE_TYPES = "routeTypes"; - MediaRouter mRouter; - private int mRouteTypes; - - private LayoutInflater mInflater; - private LauncherListener mLauncherListener; - private View.OnClickListener mExtendedSettingsListener; - private RouteAdapter mAdapter; - private ListView mListView; - private SeekBar mVolumeSlider; - private ImageView mVolumeIcon; - - final RouteComparator mComparator = new RouteComparator(); - final MediaRouterCallback mCallback = new MediaRouterCallback(); - private boolean mIgnoreSliderVolumeChanges; - private boolean mIgnoreCallbackVolumeChanges; + private View.OnClickListener mExtendedSettingsClickListener; + /** + * Creates a media route chooser dialog fragment. + *+ * All subclasses of this class must also possess a default constructor. + *
+ */ public MediaRouteChooserDialogFragment() { - setStyle(STYLE_NO_TITLE, R.style.Theme_DeviceDefault_Dialog); + setCancelable(true); + setStyle(STYLE_NORMAL, android.R.style.Theme_DeviceDefault_Dialog); } - public void setLauncherListener(LauncherListener listener) { - mLauncherListener = listener; - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mRouter = (MediaRouter) activity.getSystemService(Context.MEDIA_ROUTER_SERVICE); - mRouter.addCallback(mRouteTypes, mCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); - } - - @Override - public void onDetach() { - super.onDetach(); - if (mLauncherListener != null) { - mLauncherListener.onDetached(this); - } - if (mAdapter != null) { - mAdapter = null; - } - mInflater = null; - mRouter.removeCallback(mCallback); - mRouter = null; - } - - public void setExtendedSettingsClickListener(View.OnClickListener listener) { - mExtendedSettingsListener = listener; + public int getRouteTypes() { + Bundle args = getArguments(); + return args != null ? args.getInt(ARGUMENT_ROUTE_TYPES) : 0; } public void setRouteTypes(int types) { - mRouteTypes = types; - } - - void updateVolume() { - if (mRouter == null) return; - - final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes); - mVolumeIcon.setImageResource(selectedRoute == null || - selectedRoute.getPlaybackType() == RouteInfo.PLAYBACK_TYPE_LOCAL ? - R.drawable.ic_audio_vol : R.drawable.ic_media_route_on_holo_dark); - - mIgnoreSliderVolumeChanges = true; - - if (selectedRoute == null || - selectedRoute.getVolumeHandling() == RouteInfo.PLAYBACK_VOLUME_FIXED) { - // Disable the slider and show it at max volume. - mVolumeSlider.setMax(1); - mVolumeSlider.setProgress(1); - mVolumeSlider.setEnabled(false); - } else { - mVolumeSlider.setEnabled(true); - mVolumeSlider.setMax(selectedRoute.getVolumeMax()); - mVolumeSlider.setProgress(selectedRoute.getVolume()); - } - - mIgnoreSliderVolumeChanges = false; - } - - void changeVolume(int newValue) { - if (mIgnoreSliderVolumeChanges) return; - - final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes); - if (selectedRoute != null && - selectedRoute.getVolumeHandling() == RouteInfo.PLAYBACK_VOLUME_VARIABLE) { - final int maxVolume = selectedRoute.getVolumeMax(); - newValue = Math.max(0, Math.min(newValue, maxVolume)); - selectedRoute.requestSetVolume(newValue); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - mInflater = inflater; - final View layout = inflater.inflate(R.layout.media_route_chooser_layout, container, false); - - mVolumeIcon = (ImageView) layout.findViewById(R.id.volume_icon); - mVolumeSlider = (SeekBar) layout.findViewById(R.id.volume_slider); - updateVolume(); - mVolumeSlider.setOnSeekBarChangeListener(new VolumeSliderChangeListener()); - - if (mExtendedSettingsListener != null) { - final View extendedSettingsButton = layout.findViewById(R.id.extended_settings); - extendedSettingsButton.setVisibility(View.VISIBLE); - extendedSettingsButton.setOnClickListener(mExtendedSettingsListener); - } - - final ListView list = (ListView) layout.findViewById(R.id.list); - list.setItemsCanFocus(true); - list.setAdapter(mAdapter = new RouteAdapter()); - list.setOnItemClickListener(mAdapter); - - mListView = list; - - mAdapter.scrollToSelectedItem(); - - return layout; - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - return new RouteChooserDialog(getActivity(), getTheme()); - } - - private static class ViewHolder { - public TextView text1; - public TextView text2; - public ImageView icon; - public ImageButton expandGroupButton; - public RouteAdapter.ExpandGroupListener expandGroupListener; - public int position; - public CheckBox check; - } - - private class RouteAdapter extends BaseAdapter implements ListView.OnItemClickListener { - private static final int VIEW_TOP_HEADER = 0; - private static final int VIEW_SECTION_HEADER = 1; - private static final int VIEW_ROUTE = 2; - private static final int VIEW_GROUPING_ROUTE = 3; - private static final int VIEW_GROUPING_DONE = 4; - - private int mSelectedItemPosition = -1; - private final ArrayList