diff options
Diffstat (limited to 'packages/SystemUI/src')
22 files changed, 623 insertions, 267 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java index ce0d5f4..a311d6e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java @@ -18,13 +18,13 @@ package com.android.systemui.qs; import android.content.Context; import android.content.res.Configuration; +import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; -import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -162,6 +162,12 @@ public class QSDetailItems extends FrameLayout { view.setVisibility(mItemsVisible ? VISIBLE : INVISIBLE); final ImageView iv = (ImageView) view.findViewById(android.R.id.icon); iv.setImageResource(item.icon); + iv.getOverlay().clear(); + if (item.overlay != null) { + item.overlay.setBounds(0, 0, item.overlay.getIntrinsicWidth(), + item.overlay.getIntrinsicHeight()); + iv.getOverlay().add(item.overlay); + } final TextView title = (TextView) view.findViewById(android.R.id.title); title.setText(item.line1); final TextView summary = (TextView) view.findViewById(android.R.id.summary); @@ -213,6 +219,7 @@ public class QSDetailItems extends FrameLayout { public static class Item { public int icon; + public Drawable overlay; public String line1; public String line2; public Object tag; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java index 699240c..a920624 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -326,7 +326,12 @@ public class WifiTile extends QSTile<QSTile.SignalState> { item.line2 = mContext.getString(ap.isConfigured ? R.string.quick_settings_connected : R.string.quick_settings_connected_via_wfa); + } else if (ap.networkId >= 0) { + // TODO: Set line 2 to wifi saved string here. } + item.overlay = ap.hasSecurity + ? mContext.getDrawable(R.drawable.qs_ic_wifi_lock) + : null; items[i] = item; } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java index 4f0700e..f1bf66d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java @@ -61,15 +61,8 @@ import java.util.concurrent.atomic.AtomicBoolean; /** A proxy implementation for the recents component */ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationStartedListener { - final public static String EXTRA_FROM_HOME = "recents.triggeredOverHome"; - final public static String EXTRA_FROM_SEARCH_HOME = "recents.triggeredOverSearchHome"; - final public static String EXTRA_FROM_APP_THUMBNAIL = "recents.animatingWithThumbnail"; - final public static String EXTRA_FROM_TASK_ID = "recents.activeTaskId"; final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "recents.triggeredFromAltTab"; final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "recents.triggeredFromHomeKey"; - final public static String EXTRA_REUSE_TASK_STACK_VIEWS = "recents.reuseTaskStackViews"; - final public static String EXTRA_NUM_VISIBLE_TASKS = "recents.numVisibleTasks"; - final public static String EXTRA_NUM_VISIBLE_THUMBNAILS = "recents.numVisibleThumbnails"; final public static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation"; final public static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity"; @@ -550,7 +543,8 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack, mDummyStackView); if (opts != null) { - startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_APP_THUMBNAIL, stackVr); + startAlternateRecentsActivity(topTask, opts, false /* fromHome */, + false /* fromSearchHome */, true /* fromThumbnail */, stackVr); } else { // Fall through below to the non-thumbnail transition useThumbnailTransition = false; @@ -583,12 +577,13 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta } ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome); - startAlternateRecentsActivity(topTask, opts, - fromSearchHome ? EXTRA_FROM_SEARCH_HOME : EXTRA_FROM_HOME, stackVr); + startAlternateRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome, + false /* fromThumbnail */, stackVr); } else { // Otherwise we do the normal fade from an unknown source ActivityOptions opts = getUnknownTransitionActivityOptions(); - startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_HOME, stackVr); + startAlternateRecentsActivity(topTask, opts, true /* fromHome */, + false /* fromSearchHome */, false /* fromThumbnail */, stackVr); } } mLastToggleTime = SystemClock.elapsedRealtime(); @@ -596,21 +591,24 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta /** Starts the recents activity */ void startAlternateRecentsActivity(ActivityManager.RunningTaskInfo topTask, - ActivityOptions opts, String extraFlag, + ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail, TaskStackViewLayoutAlgorithm.VisibilityReport vr) { + // Update the configuration based on the launch options + mConfig.launchedFromHome = fromSearchHome || fromHome; + mConfig.launchedFromSearchHome = fromSearchHome; + mConfig.launchedFromAppWithThumbnail = fromThumbnail; + mConfig.launchedToTaskId = (topTask != null) ? topTask.id : -1; + mConfig.launchedWithAltTab = mTriggeredFromAltTab; + mConfig.launchedReuseTaskStackViews = mCanReuseTaskStackViews; + mConfig.launchedNumVisibleTasks = vr.numVisibleTasks; + mConfig.launchedNumVisibleThumbnails = vr.numVisibleThumbnails; + mConfig.launchedHasConfigurationChanged = false; + Intent intent = new Intent(sToggleRecentsAction); intent.setClassName(sRecentsPackage, sRecentsActivity); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Intent.FLAG_ACTIVITY_TASK_ON_HOME); - if (extraFlag != null) { - intent.putExtra(extraFlag, true); - } - intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, mTriggeredFromAltTab); - intent.putExtra(EXTRA_FROM_TASK_ID, (topTask != null) ? topTask.id : -1); - intent.putExtra(EXTRA_REUSE_TASK_STACK_VIEWS, mCanReuseTaskStackViews); - intent.putExtra(EXTRA_NUM_VISIBLE_TASKS, vr.numVisibleTasks); - intent.putExtra(EXTRA_NUM_VISIBLE_THUMBNAILS, vr.numVisibleThumbnails); if (opts != null) { mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT); } else { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index a37bc54..6baff96 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -108,8 +108,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView @Override public void run() { - // Mark Recents as no longer visible - onRecentsActivityVisibilityChanged(false); // Finish Recents if (mLaunchIntent != null) { if (mLaunchOpts != null) { @@ -133,8 +131,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(AlternateRecentsComponent.ACTION_HIDE_RECENTS_ACTIVITY)) { - // Mark Recents as no longer visible - AlternateRecentsComponent.notifyVisibilityChanged(false); if (intent.getBooleanExtra(AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false)) { // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app dismissRecentsToFocusedTaskOrHome(false); @@ -186,24 +182,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView /** Updates the set of recent tasks */ void updateRecentsTasks(Intent launchIntent) { - // Update the configuration based on the launch intent - boolean fromSearchHome = launchIntent.getBooleanExtra( - AlternateRecentsComponent.EXTRA_FROM_SEARCH_HOME, false); - int numVisibleTasks = launchIntent.getIntExtra( - AlternateRecentsComponent.EXTRA_NUM_VISIBLE_TASKS, 0); - int numVisibleThumbnails = launchIntent.getIntExtra( - AlternateRecentsComponent.EXTRA_NUM_VISIBLE_THUMBNAILS, 0); - mConfig.launchedFromHome = fromSearchHome || launchIntent.getBooleanExtra( - AlternateRecentsComponent.EXTRA_FROM_HOME, false); - mConfig.launchedFromAppWithThumbnail = launchIntent.getBooleanExtra( - AlternateRecentsComponent.EXTRA_FROM_APP_THUMBNAIL, false); - mConfig.launchedToTaskId = launchIntent.getIntExtra( - AlternateRecentsComponent.EXTRA_FROM_TASK_ID, -1); - mConfig.launchedWithAltTab = launchIntent.getBooleanExtra( - AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false); - mConfig.launchedReuseTaskStackViews = launchIntent.getBooleanExtra( - AlternateRecentsComponent.EXTRA_REUSE_TASK_STACK_VIEWS, false); - // If AlternateRecentsComponent has preloaded a load plan, then use that to prevent // reconstructing the task stack RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); @@ -218,8 +196,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options(); loadOpts.runningTaskId = mConfig.launchedToTaskId; - loadOpts.numVisibleTasks = numVisibleTasks; - loadOpts.numVisibleTaskThumbnails = numVisibleThumbnails; + loadOpts.numVisibleTasks = mConfig.launchedNumVisibleTasks; + loadOpts.numVisibleTaskThumbnails = mConfig.launchedNumVisibleThumbnails; loader.loadTasks(this, plan, loadOpts); SpaceNode root = plan.getSpaceNode(); @@ -237,9 +215,9 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent, ActivityOptions.makeCustomAnimation(this, - fromSearchHome ? R.anim.recents_to_search_launcher_enter : + mConfig.launchedFromSearchHome ? R.anim.recents_to_search_launcher_enter : R.anim.recents_to_launcher_enter, - fromSearchHome ? R.anim.recents_to_search_launcher_exit : + mConfig.launchedFromSearchHome ? R.anim.recents_to_search_launcher_exit : R.anim.recents_to_launcher_exit)); // Mark the task that is the launch target @@ -403,12 +381,12 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub); mDebugOverlayStub = (ViewStub) findViewById(R.id.debug_overlay_stub); mScrimViews = new SystemBarScrimViews(this, mConfig); + mStatusBar = ((SystemUIApplication) getApplication()) + .getComponent(PhoneStatusBar.class); inflateDebugOverlay(); // Bind the search app widget when we first start up bindSearchBarAppWidget(); - // Update the recent tasks - updateRecentsTasks(getIntent()); // Register the broadcast receiver to handle messages when the screen is turned off IntentFilter filter = new IntentFilter(); @@ -424,17 +402,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } catch (InvocationTargetException e) { e.printStackTrace(); } - - // Update if we are getting a configuration change - if (savedInstanceState != null) { - // Update RecentsConfiguration - mConfig.updateOnConfigurationChange(); - // Trigger the enter animation - onEnterAnimationTriggered(); - } - - mStatusBar = ((SystemUIApplication) getApplication()) - .getComponent(PhoneStatusBar.class); } /** Inflates the debug overlay if debug mode is enabled. */ @@ -449,14 +416,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } } - /** Handles changes to the activity visibility. */ - void onRecentsActivityVisibilityChanged(boolean visible) { - if (!visible) { - AlternateRecentsComponent.notifyVisibilityChanged(visible); - } - mVisible = visible; - } - @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); @@ -469,14 +428,13 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView if (mDebugOverlay != null) { mDebugOverlay.clear(); } - - // Update the recent tasks - updateRecentsTasks(intent); } @Override protected void onStart() { super.onStart(); + mVisible = true; + AlternateRecentsComponent.notifyVisibilityChanged(true); // Register the broadcast receiver to handle messages from our service IntentFilter filter = new IntentFilter(); @@ -487,19 +445,16 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // Register any broadcast receivers for the task loader RecentsTaskLoader.getInstance().registerReceivers(this, mRecentsView); - } - - @Override - protected void onResume() { - super.onResume(); - // Mark Recents as visible - onRecentsActivityVisibilityChanged(true); + // Update the recent tasks + updateRecentsTasks(getIntent()); } @Override protected void onStop() { super.onStop(); + mVisible = false; + AlternateRecentsComponent.notifyVisibilityChanged(false); // Notify the views that we are no longer visible mRecentsView.onRecentsHidden(); @@ -641,8 +596,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView @Override public void onTaskViewClicked() { - // Mark recents as no longer visible - onRecentsActivityVisibilityChanged(false); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index 2b33d14..52e7e7f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -124,8 +124,12 @@ public class RecentsConfiguration { public boolean launchedWithNoRecentTasks; public boolean launchedFromAppWithThumbnail; public boolean launchedFromHome; + public boolean launchedFromSearchHome; public boolean launchedReuseTaskStackViews; + public boolean launchedHasConfigurationChanged; public int launchedToTaskId; + public int launchedNumVisibleTasks; + public int launchedNumVisibleThumbnails; /** Misc **/ public boolean useHardwareLayers; @@ -308,12 +312,10 @@ public class RecentsConfiguration { /** Called when the configuration has changed, and we want to reset any configuration specific * members. */ public void updateOnConfigurationChange() { - launchedWithAltTab = false; - launchedWithNoRecentTasks = false; - launchedFromAppWithThumbnail = false; - launchedFromHome = false; + // Reset this flag on configuration change to ensure that we recreate new task views launchedReuseTaskStackViews = false; - launchedToTaskId = -1; + // Set this flag to indicate that the configuration has changed since Recents last launched + launchedHasConfigurationChanged = true; } /** Returns whether the search bar app widget exists. */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 33a36f6..169683f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -715,14 +715,20 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mStartEnterAnimationContext = null; } - // When Alt-Tabbing, we scroll to and focus the previous task + // When Alt-Tabbing, focus the previous task (but leave the animation until we finish the + // enter animation). if (mConfig.launchedWithAltTab) { - if (mConfig.launchedFromHome) { - focusTask(Math.max(0, mStack.getTaskCount() - 1), false, true); + if (mConfig.launchedFromAppWithThumbnail) { + focusTask(Math.max(0, mStack.getTaskCount() - 2), false, + mConfig.launchedHasConfigurationChanged); } else { - focusTask(Math.max(0, mStack.getTaskCount() - 2), false, true); + focusTask(Math.max(0, mStack.getTaskCount() - 1), false, + mConfig.launchedHasConfigurationChanged); } } + + // Start dozing + mUIDozeTrigger.startDozing(); } /** Requests this task stacks to start it's enter-recents animation */ @@ -767,16 +773,27 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal @Override public void run() { mStartEnterAnimationCompleted = true; - // Start dozing - mUIDozeTrigger.startDozing(); - // Focus the first view if accessibility is enabled + // Poke the dozer to restart the trigger after the animation completes + mUIDozeTrigger.poke(); + RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); SystemServicesProxy ssp = loader.getSystemServicesProxy(); int childCount = getChildCount(); - if (childCount > 0 && ssp.isTouchExplorationEnabled()) { - TaskView tv = ((TaskView) getChildAt(childCount - 1)); - tv.requestAccessibilityFocus(); - mPrevAccessibilityFocusedIndex = mStack.indexOfTask(tv.getTask()); + if (childCount > 0) { + // Focus the first view if accessibility is enabled + if (ssp.isTouchExplorationEnabled()) { + TaskView tv = ((TaskView) getChildAt(childCount - 1)); + tv.requestAccessibilityFocus(); + mPrevAccessibilityFocusedIndex = mStack.indexOfTask(tv.getTask()); + } + } + + // Start the focus animation when alt-tabbing + if (mConfig.launchedWithAltTab && !mConfig.launchedHasConfigurationChanged) { + View tv = getChildAt(mFocusedTaskIndex); + if (tv != null) { + ((TaskView) tv).setFocusedTask(true); + } } } }); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java index 26fbbf4..49b9129 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java @@ -135,7 +135,6 @@ public class TaskStackViewLayoutAlgorithm { // Update the task offsets float pAtBackMostCardTop = 0.5f; float pAtFrontMostCardTop = pAtBackMostCardTop; - float pAtSecondFrontMostCardTop = pAtBackMostCardTop; int taskCount = tasks.size(); for (int i = 0; i < taskCount; i++) { Task task = tasks.get(i); @@ -145,25 +144,19 @@ public class TaskStackViewLayoutAlgorithm { // Increment the peek height float pPeek = task.group.isFrontMostTask(task) ? pBetweenAffiliateOffset : pWithinAffiliateOffset; - pAtSecondFrontMostCardTop = pAtFrontMostCardTop; pAtFrontMostCardTop += pPeek; } } mMaxScrollP = pAtFrontMostCardTop - ((1f - pTaskHeightOffset - pNavBarOffset)); mMinScrollP = tasks.size() == 1 ? Math.max(mMaxScrollP, 0f) : 0f; - if (launchedWithAltTab) { - if (launchedFromHome) { - // Center the top most task, since that will be focused first - mInitialScrollP = pAtSecondFrontMostCardTop - 0.5f; - } else { - // Center the second top most task, since that will be focused first - mInitialScrollP = pAtSecondFrontMostCardTop - 0.5f; - } + if (launchedWithAltTab && launchedFromHome) { + // Center the top most task, since that will be focused first + mInitialScrollP = mMaxScrollP; } else { mInitialScrollP = pAtFrontMostCardTop - 0.825f; } - mInitialScrollP = Math.max(0, mInitialScrollP); + mInitialScrollP = Math.min(mMaxScrollP, Math.max(0, mInitialScrollP)); } /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index de5974f..faa728d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -235,7 +235,9 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask, boolean occludesLaunchTarget, int offscreenY) { int initialDim = getDim(); - if (mConfig.launchedFromAppWithThumbnail) { + if (mConfig.launchedHasConfigurationChanged) { + // Just load the views as-is + } else if (mConfig.launchedFromAppWithThumbnail) { if (isTaskViewLaunchTargetTask) { // Set the dim to 0 so we can animate it in initialDim = 0; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java index 464d007..05f6f40 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java @@ -237,15 +237,17 @@ public class TaskViewHeader extends FrameLayout { /** Animates this task bar if the user does not interact with the stack after a certain time. */ void startNoUserInteractionAnimation() { - mDismissButton.setVisibility(View.VISIBLE); - mDismissButton.setAlpha(0f); - mDismissButton.animate() - .alpha(1f) - .setStartDelay(0) - .setInterpolator(mConfig.fastOutLinearInInterpolator) - .setDuration(mConfig.taskViewEnterFromAppDuration) - .withLayer() - .start(); + if (mDismissButton.getVisibility() != View.VISIBLE) { + mDismissButton.setVisibility(View.VISIBLE); + mDismissButton.setAlpha(0f); + mDismissButton.animate() + .alpha(1f) + .setStartDelay(0) + .setInterpolator(mConfig.fastOutLinearInInterpolator) + .setDuration(mConfig.taskViewEnterFromAppDuration) + .withLayer() + .start(); + } } /** Mark this task view that the user does has not interacted with the stack after a certain time. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index 0faad21..914b3d8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -124,6 +124,7 @@ public class NotificationContentView extends FrameLayout { mContractedChild = child; mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child); selectLayout(false /* animate */, true /* force */); + mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */); } public void setExpandedChild(View child) { @@ -245,6 +246,7 @@ public class NotificationContentView extends FrameLayout { public void notifyContentUpdated() { selectLayout(false /* animate */, true /* force */); if (mContractedChild != null) { + mContractedWrapper.notifyContentUpdated(); mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java index 5b6e1cd..fbcba0b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java @@ -26,6 +26,7 @@ import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; import android.view.View; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; @@ -40,17 +41,18 @@ import com.android.systemui.statusbar.phone.NotificationPanelView; */ public class NotificationTemplateViewWrapper extends NotificationViewWrapper { - private final ViewInvertHelper mInvertHelper; - private final ImageView mIcon; - protected final ImageView mPicture; private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix(); private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter( 0, PorterDuff.Mode.SRC_ATOP); private final int mIconDarkAlpha; - private final int mIconBackgroundColor; private final int mIconBackgroundDarkColor; private final Interpolator mLinearOutSlowInInterpolator; + private int mIconBackgroundColor; + private ViewInvertHelper mInvertHelper; + private ImageView mIcon; + protected ImageView mPicture; + protected NotificationTemplateViewWrapper(Context ctx, View view) { super(view); mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha); @@ -58,12 +60,16 @@ public class NotificationTemplateViewWrapper extends NotificationViewWrapper { ctx.getResources().getColor(R.color.doze_small_icon_background_color); mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx, android.R.interpolator.linear_out_slow_in); - View mainColumn = view.findViewById(com.android.internal.R.id.notification_main_column); + resolveViews(); + } + + private void resolveViews() { + View mainColumn = mView.findViewById(com.android.internal.R.id.notification_main_column); mInvertHelper = mainColumn != null ? new ViewInvertHelper(mainColumn, NotificationPanelView.DOZE_ANIMATION_DURATION) : null; - ImageView largeIcon = (ImageView) view.findViewById(com.android.internal.R.id.icon); - ImageView rightIcon = (ImageView) view.findViewById(com.android.internal.R.id.right_icon); + ImageView largeIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon); + ImageView rightIcon = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon); mIcon = resolveIcon(largeIcon, rightIcon); mPicture = resolvePicture(largeIcon); mIconBackgroundColor = resolveBackgroundColor(mIcon); @@ -92,6 +98,14 @@ public class NotificationTemplateViewWrapper extends NotificationViewWrapper { } @Override + public void notifyContentUpdated() { + super.notifyContentUpdated(); + + // Reinspect the notification. + resolveViews(); + } + + @Override public void setDark(boolean dark, boolean fade, long delay) { if (mInvertHelper != null) { if (fade) { @@ -180,7 +194,13 @@ public class NotificationTemplateViewWrapper extends NotificationViewWrapper { private void updateIconColorFilter(ImageView target, float intensity) { int color = interpolateColor(mIconBackgroundColor, mIconBackgroundDarkColor, intensity); mIconColorFilter.setColor(color); - target.getBackground().mutate().setColorFilter(mIconColorFilter); + Drawable background = target.getBackground(); + + // The notification might have been modified during the animation, so background might be + // null here. + if (background != null) { + background.mutate().setColorFilter(mIconColorFilter); + } } private void updateIconAlpha(ImageView target, boolean dark) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java index 0a02573..78b9739 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java @@ -53,4 +53,9 @@ public abstract class NotificationViewWrapper { * @param delay if fading, the delay of the animation */ public abstract void setDark(boolean dark, boolean fade, long delay); + + /** + * Notifies this wrapper that the content of the view might have changed. + */ + public void notifyContentUpdated() {} } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java index 8e50abe..8e35ee9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java @@ -277,9 +277,13 @@ public class SignalClusterView mWifiStrengthId)); boolean anyMobileVisible = false; + int firstMobileTypeId = 0; for (PhoneState state : mPhoneStates) { if (state.apply(anyMobileVisible)) { - anyMobileVisible = true; + if (!anyMobileVisible) { + firstMobileTypeId = state.mMobileTypeId; + anyMobileVisible = true; + } } } @@ -298,7 +302,7 @@ public class SignalClusterView mWifiAirplaneSpacer.setVisibility(View.GONE); } - if ((anyMobileVisible || mNoSimsVisible) && mWifiVisible) { + if (((anyMobileVisible && firstMobileTypeId != 0) || mNoSimsVisible) && mWifiVisible) { mWifiSignalSpacer.setVisibility(View.VISIBLE); } else { mWifiSignalSpacer.setVisibility(View.GONE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 08844f3..d2dc425 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -1629,6 +1629,7 @@ public class NotificationPanelView extends PanelView implements } else { mSecureCameraLaunchManager.startSecureCameraLaunch(); } + mStatusBar.startLaunchTransitionTimeout(); mBlockTouches = true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index ec2d30c..8b328aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -203,8 +203,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000; private static final int MSG_CLOSE_PANELS = 1001; private static final int MSG_OPEN_SETTINGS_PANEL = 1002; + private static final int MSG_LAUNCH_TRANSITION_TIMEOUT = 1003; // 1020-1040 reserved for BaseStatusBar + // Time after we abort the launch transition. + private static final long LAUNCH_TRANSITION_TIMEOUT_MS = 5000; + private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true; private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService @@ -2196,6 +2200,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, escalateHeadsUp(); setHeadsUpVisibility(false); break; + case MSG_LAUNCH_TRANSITION_TIMEOUT: + onLaunchTransitionTimeout(); + break; } } } @@ -3528,12 +3535,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (mLaunchTransitionFadingAway) { mNotificationPanel.animate().cancel(); mNotificationPanel.setAlpha(1f); - if (mLaunchTransitionEndRunnable != null) { - mLaunchTransitionEndRunnable.run(); - } - mLaunchTransitionEndRunnable = null; + runLaunchTransitionEndRunnable(); mLaunchTransitionFadingAway = false; } + mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); setBarState(StatusBarState.KEYGUARD); updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */); if (!mScreenOnFromKeyguard) { @@ -3574,6 +3579,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, */ public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading, Runnable endRunnable) { + mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); mLaunchTransitionEndRunnable = endRunnable; Runnable hideRunnable = new Runnable() { @Override @@ -3592,10 +3598,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, @Override public void run() { mNotificationPanel.setAlpha(1); - if (mLaunchTransitionEndRunnable != null) { - mLaunchTransitionEndRunnable.run(); - } - mLaunchTransitionEndRunnable = null; + runLaunchTransitionEndRunnable(); mLaunchTransitionFadingAway = false; } }); @@ -3609,6 +3612,32 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } /** + * Starts the timeout when we try to start the affordances on Keyguard. We usually rely that + * Keyguard goes away via fadeKeyguardAfterLaunchTransition, however, that might not happen + * because the launched app crashed or something else went wrong. + */ + public void startLaunchTransitionTimeout() { + mHandler.sendEmptyMessageDelayed(MSG_LAUNCH_TRANSITION_TIMEOUT, + LAUNCH_TRANSITION_TIMEOUT_MS); + } + + private void onLaunchTransitionTimeout() { + Log.w(TAG, "Launch transition: Timeout!"); + mNotificationPanel.resetViews(); + } + + private void runLaunchTransitionEndRunnable() { + if (mLaunchTransitionEndRunnable != null) { + Runnable r = mLaunchTransitionEndRunnable; + + // mLaunchTransitionEndRunnable might call showKeyguard, which would execute it again, + // which would lead to infinite recursion. Protect against it. + mLaunchTransitionEndRunnable = null; + r.run(); + } + } + + /** * @return true if we would like to stay in the shade, false if it should go away entirely */ public boolean hideKeyguard() { @@ -3631,6 +3660,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (mQSPanel != null) { mQSPanel.refreshAllTiles(); } + mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); return staying; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 77da70a..f4edab5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -199,7 +199,7 @@ public class StatusBarKeyguardViewManager { new Runnable() { @Override public void run() { - mStatusBarWindowManager.setKeyguardOccluded(true); + mStatusBarWindowManager.setKeyguardOccluded(mOccluded); reset(); } }); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java index 80fec5b..076cfe2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java @@ -19,15 +19,21 @@ package com.android.systemui.statusbar.policy; import static android.bluetooth.BluetoothAdapter.ERROR; import static com.android.systemui.statusbar.policy.BluetoothUtil.connectionStateToString; import static com.android.systemui.statusbar.policy.BluetoothUtil.deviceToString; -import static com.android.systemui.statusbar.policy.BluetoothUtil.profileStateToString; import static com.android.systemui.statusbar.policy.BluetoothUtil.profileToString; import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidToProfile; import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidToString; import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidsToString; +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothA2dpSink; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHeadset; +import android.bluetooth.BluetoothHeadsetClient; +import android.bluetooth.BluetoothInputDevice; import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothMap; +import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile.ServiceListener; import android.content.BroadcastReceiver; @@ -38,24 +44,37 @@ import android.os.ParcelUuid; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; -import android.util.SparseBooleanArray; +import android.util.SparseArray; import com.android.systemui.statusbar.policy.BluetoothUtil.Profile; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; import java.util.Set; public class BluetoothControllerImpl implements BluetoothController { private static final String TAG = "BluetoothController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + // This controls the order in which we check the states. Since a device can only have + // one state on screen, but can have multiple profiles, the later states override the + // value of earlier states. So if a device has a profile in CONNECTING and one in + // CONNECTED, it will show as CONNECTED, theoretically this shouldn't really happen often, + // but seemed worth noting. + private static final int[] CONNECTION_STATES = { + BluetoothProfile.STATE_DISCONNECTED, + BluetoothProfile.STATE_DISCONNECTING, + BluetoothProfile.STATE_CONNECTING, + BluetoothProfile.STATE_CONNECTED, + }; private final Context mContext; private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); private final BluetoothAdapter mAdapter; private final Receiver mReceiver = new Receiver(); private final ArrayMap<BluetoothDevice, DeviceInfo> mDeviceInfo = new ArrayMap<>(); + private final SparseArray<BluetoothProfile> mProfiles = new SparseArray<>(); private boolean mEnabled; private boolean mConnecting; @@ -73,7 +92,8 @@ public class BluetoothControllerImpl implements BluetoothController { mReceiver.register(); setAdapterState(mAdapter.getState()); - updateBondedBluetoothDevices(); + updateBluetoothDevices(); + bindAllProfiles(); } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { @@ -83,6 +103,7 @@ public class BluetoothControllerImpl implements BluetoothController { pw.print(" mConnecting="); pw.println(mConnecting); pw.print(" mLastDevice="); pw.println(mLastDevice); pw.print(" mCallbacks.size="); pw.println(mCallbacks.size()); + pw.print(" mProfiles="); pw.println(profilesToString(mProfiles)); pw.print(" mDeviceInfo.size="); pw.println(mDeviceInfo.size()); for (int i = 0; i < mDeviceInfo.size(); i++) { final BluetoothDevice device = mDeviceInfo.keyAt(i); @@ -95,7 +116,22 @@ public class BluetoothControllerImpl implements BluetoothController { private static String infoToString(DeviceInfo info) { return info == null ? null : ("connectionState=" + - connectionStateToString(info.connectionState) + ",bonded=" + info.bonded); + connectionStateToString(info.connectionState) + ",bonded=" + info.bonded + + ",profiles=" + profilesToString(info.connectedProfiles)); + } + + private static String profilesToString(SparseArray<?> profiles) { + final int N = profiles.size(); + final StringBuffer buffer = new StringBuffer(); + buffer.append('['); + for (int i = 0; i < N; i++) { + if (i != 0) { + buffer.append(','); + } + buffer.append(BluetoothUtil.profileToString(profiles.keyAt(i))); + } + buffer.append(']'); + return buffer.toString(); } public void addStateChangedCallback(Callback cb) { @@ -178,6 +214,7 @@ public class BluetoothControllerImpl implements BluetoothController { private void connect(PairedDevice pd, final boolean connect) { if (mAdapter == null || pd == null || pd.tag == null) return; final BluetoothDevice device = (BluetoothDevice) pd.tag; + final DeviceInfo info = mDeviceInfo.get(device); final String action = connect ? "connect" : "disconnect"; if (DEBUG) Log.d(TAG, action + " " + deviceToString(device)); final ParcelUuid[] uuids = device.getUuids(); @@ -185,43 +222,35 @@ public class BluetoothControllerImpl implements BluetoothController { Log.w(TAG, "No uuids returned, aborting " + action + " for " + deviceToString(device)); return; } - final SparseBooleanArray profiles = new SparseBooleanArray(); - for (ParcelUuid uuid : uuids) { - final int profile = uuidToProfile(uuid); - if (profile == 0) { - Log.w(TAG, "Device " + deviceToString(device) + " has an unsupported uuid: " - + uuidToString(uuid)); - continue; - } - final int profileState = mAdapter.getProfileConnectionState(profile); - if (DEBUG && !profiles.get(profile)) Log.d(TAG, "Profile " + profileToString(profile) - + " state = " + profileStateToString(profileState)); - final boolean connected = profileState == BluetoothProfile.STATE_CONNECTED; - if (connect != connected) { - profiles.put(profile, true); + SparseArray<Boolean> profiles = new SparseArray<>(); + if (connect) { + // When connecting add every profile we can recognize by uuid. + for (ParcelUuid uuid : uuids) { + final int profile = uuidToProfile(uuid); + if (profile == 0) { + Log.w(TAG, "Device " + deviceToString(device) + " has an unsupported uuid: " + + uuidToString(uuid)); + continue; + } + final boolean connected = info.connectedProfiles.get(profile, false); + if (!connected) { + profiles.put(profile, true); + } } + } else { + // When disconnecting, just add every profile we know they are connected to. + profiles = info.connectedProfiles; } for (int i = 0; i < profiles.size(); i++) { final int profile = profiles.keyAt(i); - mAdapter.getProfileProxy(mContext, new ServiceListener() { - @Override - public void onServiceConnected(int profile, BluetoothProfile proxy) { - if (DEBUG) Log.d(TAG, "onServiceConnected " + profileToString(profile)); - final Profile p = BluetoothUtil.getProfile(proxy); - if (p == null) { - Log.w(TAG, "Unable get get Profile for " + profileToString(profile)); - } else { - final boolean ok = connect ? p.connect(device) : p.disconnect(device); - if (DEBUG) Log.d(TAG, action + " " + profileToString(profile) + " " - + (ok ? "succeeded" : "failed")); - } - } - - @Override - public void onServiceDisconnected(int profile) { - if (DEBUG) Log.d(TAG, "onServiceDisconnected " + profileToString(profile)); - } - }, profile); + if (mProfiles.indexOfKey(profile) >= 0) { + final Profile p = BluetoothUtil.getProfile(mProfiles.get(profile)); + final boolean ok = connect ? p.connect(device) : p.disconnect(device); + if (DEBUG) Log.d(TAG, action + " " + profileToString(profile) + " " + + (ok ? "succeeded" : "failed")); + } else { + Log.w(TAG, "Unable get get Profile for " + profileToString(profile)); + } } } @@ -230,11 +259,13 @@ public class BluetoothControllerImpl implements BluetoothController { return mLastDevice != null ? mLastDevice.getAliasName() : null; } - private void updateBondedBluetoothDevices() { + private void updateBluetoothDevices() { if (mAdapter == null) return; final Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); for (DeviceInfo info : mDeviceInfo.values()) { info.bonded = false; + info.connectionState = ERROR; + info.connectedProfiles.clear(); } int bondedCount = 0; BluetoothDevice lastBonded = null; @@ -248,12 +279,62 @@ public class BluetoothControllerImpl implements BluetoothController { } } } + final int N = mProfiles.size(); + final int[] connectionType = new int[1]; + for (int i = 0; i < CONNECTION_STATES.length; i++) { + connectionType[0] = CONNECTION_STATES[i]; + for (int j = 0; j < N; j++) { + int profile = mProfiles.keyAt(j); + List<BluetoothDevice> devices = mProfiles.get(profile) + .getDevicesMatchingConnectionStates(connectionType); + for (int k = 0; k < devices.size(); k++) { + DeviceInfo info = mDeviceInfo.get(devices.get(k)); + info.connectionState = CONNECTION_STATES[i]; + if (CONNECTION_STATES[i] == BluetoothProfile.STATE_CONNECTED) { + info.connectedProfiles.put(profile, true); + } + } + } + } if (mLastDevice == null && bondedCount == 1) { mLastDevice = lastBonded; } + // If we are no longer connected to the current device, see if we are connected to + // something else, so we don't display a name we aren't connected to. + if (mLastDevice != null && + mDeviceInfo.get(mLastDevice).connectionState != BluetoothProfile.STATE_CONNECTED) { + // Make sure we don't keep this device while it isn't connected. + mLastDevice = null; + // Look for anything else connected. + final int size = mDeviceInfo.size(); + for (int i = 0; i < size; i++) { + BluetoothDevice device = mDeviceInfo.keyAt(i); + DeviceInfo info = mDeviceInfo.valueAt(i); + if (info.connectionState == BluetoothProfile.STATE_CONNECTED) { + mLastDevice = device; + break; + } + } + } firePairedDevicesChanged(); } + private void bindAllProfiles() { + // Note: This needs to contain all of the types that can be returned by BluetoothUtil + // otherwise we can't find the profiles we need when we connect/disconnect. + mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP); + mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP_SINK); + mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.AVRCP_CONTROLLER); + mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEADSET); + mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEADSET_CLIENT); + mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.INPUT_DEVICE); + mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.MAP); + mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.PAN); + // Note Health is not in this list because health devices aren't 'connected'. + // If profiles are expanded to use more than just connection state and connect/disconnect + // then it should be added. + } + private void firePairedDevicesChanged() { for (Callback cb : mCallbacks) { cb.onBluetoothPairedDevicesChanged(); @@ -283,6 +364,20 @@ public class BluetoothControllerImpl implements BluetoothController { cb.onBluetoothStateChange(mEnabled, mConnecting); } + private final ServiceListener mProfileListener = new ServiceListener() { + @Override + public void onServiceDisconnected(int profile) { + mProfiles.remove(profile); + updateBluetoothDevices(); + } + + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + mProfiles.put(profile, proxy); + updateBluetoothDevices(); + } + }; + private final class Receiver extends BroadcastReceiver { public void register() { final IntentFilter filter = new IntentFilter(); @@ -290,6 +385,15 @@ public class BluetoothControllerImpl implements BluetoothController { filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); filter.addAction(BluetoothDevice.ACTION_ALIAS_CHANGED); + filter.addAction(BluetoothDevice.ACTION_CLASS_CHANGED); + filter.addAction(BluetoothDevice.ACTION_UUID); + filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); + filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); + filter.addAction(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED); + filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED); + filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED); + filter.addAction(BluetoothMap.ACTION_CONNECTION_STATE_CHANGED); + filter.addAction(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED); mContext.registerReceiver(this, filter); } @@ -301,16 +405,13 @@ public class BluetoothControllerImpl implements BluetoothController { setAdapterState(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, ERROR)); if (DEBUG) Log.d(TAG, "ACTION_STATE_CHANGED " + mEnabled); } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) { - final DeviceInfo info = updateInfo(device); + updateInfo(device); final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, ERROR); - if (state != ERROR) { - info.connectionState = state; - } mLastDevice = device; if (DEBUG) Log.d(TAG, "ACTION_CONNECTION_STATE_CHANGED " + connectionStateToString(state) + " " + deviceToString(device)); - setConnecting(info.connectionState == BluetoothAdapter.STATE_CONNECTING); + setConnecting(state == BluetoothAdapter.STATE_CONNECTING); } else if (action.equals(BluetoothDevice.ACTION_ALIAS_CHANGED)) { updateInfo(device); mLastDevice = device; @@ -318,7 +419,8 @@ public class BluetoothControllerImpl implements BluetoothController { if (DEBUG) Log.d(TAG, "ACTION_BOND_STATE_CHANGED " + device); // we'll update all bonded devices below } - updateBondedBluetoothDevices(); + // Always update bluetooth devices state. + updateBluetoothDevices(); } } @@ -332,5 +434,6 @@ public class BluetoothControllerImpl implements BluetoothController { private static class DeviceInfo { int connectionState = BluetoothAdapter.STATE_DISCONNECTED; boolean bonded; // per getBondedDevices + SparseArray<Boolean> connectedProfiles = new SparseArray<>(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothUtil.java index 1b4be85..ed8ac2c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothUtil.java @@ -36,7 +36,10 @@ public class BluetoothUtil { if (profile == BluetoothProfile.HEADSET) return "HEADSET"; if (profile == BluetoothProfile.A2DP) return "A2DP"; if (profile == BluetoothProfile.AVRCP_CONTROLLER) return "AVRCP_CONTROLLER"; - return "UNKNOWN"; + if (profile == BluetoothProfile.PAN) return "PAN"; + if (profile == BluetoothProfile.INPUT_DEVICE) return "INPUT_DEVICE"; + if (profile == BluetoothProfile.MAP) return "MAP"; + return "UNKNOWN(" + profile + ")"; } public static String profileStateToString(int state) { @@ -106,6 +109,11 @@ public class BluetoothUtil { if (BluetoothUuid.AvrcpController.equals(uuid)) return BluetoothProfile.AVRCP_CONTROLLER; + if (BluetoothUuid.Hid.equals(uuid)) return BluetoothProfile.INPUT_DEVICE; + if (BluetoothUuid.Hogp.equals(uuid)) return BluetoothProfile.INPUT_DEVICE; + + if (BluetoothUuid.NAP.equals(uuid)) return BluetoothProfile.PAN; + return 0; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java index 30da9cb..af51266 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java @@ -196,6 +196,7 @@ public class MobileDataControllerImpl implements NetworkController.MobileDataCon } public void setMobileDataEnabled(boolean enabled) { + Log.d(TAG, "setMobileDataEnabled: enabled=" + enabled); mTelephonyManager.setDataEnabled(enabled); if (mCallback != null) { mCallback.onMobileDataEnabled(enabled); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 6431ab5..3397a38 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -272,7 +272,7 @@ public class NetworkControllerImpl extends BroadcastReceiver if (mMobileSignalControllers.containsKey(dataSubId)) { return mMobileSignalControllers.get(dataSubId); } - Log.e(TAG, "Cannot find controller for data sub: " + dataSubId); + if (DEBUG) Log.e(TAG, "Cannot find controller for data sub: " + dataSubId); return mDefaultSignalController; } @@ -293,7 +293,7 @@ public class NetworkControllerImpl extends BroadcastReceiver if (mMobileSignalControllers.containsKey(voiceSubId)) { return mMobileSignalControllers.get(voiceSubId).isEmergencyOnly(); } - Log.e(TAG, "Cannot find controller for voice sub: " + voiceSubId); + if (DEBUG) Log.e(TAG, "Cannot find controller for voice sub: " + voiceSubId); // Something is wrong, better assume we can't make calls... return true; } @@ -483,6 +483,10 @@ public class NetworkControllerImpl extends BroadcastReceiver cachedControllers.get(key).unregisterListener(); } } + // There may be new MobileSignalControllers around, make sure they get the current + // inet condition and airplane mode. + pushConnectivityToSignals(); + updateAirplaneMode(true /* force */); } private boolean hasCorrectMobileControllers(List<SubscriptionInfo> allSubscriptions) { @@ -577,6 +581,13 @@ public class NetworkControllerImpl extends BroadcastReceiver mBluetoothTethered = mConnectedTransports.get(TRANSPORT_BLUETOOTH); mEthernetConnected = mConnectedTransports.get(TRANSPORT_ETHERNET); + pushConnectivityToSignals(); + } + + /** + * Pushes the current connectivity state to all SignalControllers. + */ + private void pushConnectivityToSignals() { // We want to update all the icons, all at once, for any condition change for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { mobileSignalController.setInetCondition( diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java index d3a8fc0..acdcfc1 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java @@ -124,8 +124,7 @@ public class VolumePanel extends Handler implements DemoMode { private static final int MSG_ZEN_MODE_AVAILABLE_CHANGED = 13; private static final int MSG_USER_ACTIVITY = 14; private static final int MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED = 15; - private static final int MSG_ZEN_MODE_CHANGED = 16; - private static final int MSG_INTERNAL_RINGER_MODE_CHANGED = 17; + private static final int MSG_INTERNAL_RINGER_MODE_CHANGED = 16; // Pseudo stream type for master volume private static final int STREAM_MASTER = -100; @@ -511,6 +510,9 @@ public class VolumePanel extends Handler implements DemoMode { pw.println(); } } + if (mZenPanel != null) { + mZenPanel.dump(fd, pw, args); + } } private void initZenModePanel() { @@ -723,7 +725,7 @@ public class VolumePanel extends Handler implements DemoMode { mSliderPanel.addView(active.group); mActiveStreamType = activeStreamType; active.group.setVisibility(View.VISIBLE); - updateSlider(active); + updateSlider(active, true /*forceReloadIcon*/); updateTimeoutDelay(); updateZenPanelVisible(); } @@ -799,11 +801,12 @@ public class VolumePanel extends Handler implements DemoMode { } /** Update the mute and progress state of a slider */ - private void updateSlider(StreamControl sc) { + private void updateSlider(StreamControl sc, boolean forceReloadIcon) { updateSliderProgress(sc, -1); final boolean muted = isMuted(sc.streamType); - // Force reloading the image resource - sc.icon.setImageDrawable(null); + if (forceReloadIcon) { + sc.icon.setImageDrawable(null); + } updateSliderIcon(sc, muted); updateSliderEnabled(sc, muted, false); updateSliderSuppressor(sc); @@ -907,11 +910,18 @@ public class VolumePanel extends Handler implements DemoMode { } } - public void updateStates() { + private void updateStates() { final int count = mSliderPanel.getChildCount(); for (int i = 0; i < count; i++) { StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag(); - updateSlider(sc); + updateSlider(sc, true /*forceReloadIcon*/); + } + } + + private void updateActiveSlider() { + final StreamControl active = mStreamControls.get(mActiveStreamType); + if (active != null) { + updateSlider(active, false /*forceReloadIcon*/); } } @@ -1449,12 +1459,11 @@ public class VolumePanel extends Handler implements DemoMode { break; } - case MSG_ZEN_MODE_CHANGED: case MSG_RINGER_MODE_CHANGED: case MSG_INTERNAL_RINGER_MODE_CHANGED: case MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED: { if (isShowing()) { - updateStates(); + updateActiveSlider(); } break; } @@ -1563,10 +1572,6 @@ public class VolumePanel extends Handler implements DemoMode { mNotificationEffectsSuppressor = mZenController.getEffectsSuppressor(); sendEmptyMessage(MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED); } - - public void onZenChanged(int zen) { - sendEmptyMessage(MSG_ZEN_MODE_CHANGED); - } }; private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() { @@ -1591,6 +1596,7 @@ public class VolumePanel extends Handler implements DemoMode { public void start(StreamControl sc) { if (sc == null) throw new IllegalArgumentException(); + if (LOGD) Log.d(mTag, "Secondary icon animation start"); if (mTarget != null) { cancel(); } @@ -1643,6 +1649,7 @@ public class VolumePanel extends Handler implements DemoMode { @Override public void run() { if (mTarget == null) return; + if (LOGD) Log.d(mTag, "Secondary icon animation complete, show notification slider"); mAudioManager.forceVolumeControlStream(StreamResources.NotificationStream.streamType); mAudioManager.adjustStreamVolume(StreamResources.NotificationStream.streamType, AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI); diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java index 6ed24e0..e250ec7 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java +++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java @@ -17,13 +17,16 @@ package com.android.systemui.volume; import android.animation.LayoutTransition; +import android.animation.LayoutTransition.TransitionListener; import android.app.ActivityManager; +import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.res.Resources; import android.net.Uri; +import android.os.AsyncTask; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -32,6 +35,7 @@ import android.provider.Settings.Global; import android.service.notification.Condition; import android.service.notification.ZenModeConfig; import android.text.TextUtils; +import android.util.ArraySet; import android.util.AttributeSet; import android.util.Log; import android.util.MathUtils; @@ -50,6 +54,8 @@ import android.widget.TextView; import com.android.systemui.R; import com.android.systemui.statusbar.policy.ZenModeController; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.Arrays; import java.util.Objects; @@ -67,8 +73,7 @@ public class ZenModePanel extends LinearLayout { private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1]; private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60); private static final int FOREVER_CONDITION_INDEX = 0; - private static final int TIME_CONDITION_INDEX = 1; - private static final int FIRST_CONDITION_INDEX = 2; + private static final int COUNTDOWN_CONDITION_INDEX = 1; public static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS); @@ -81,6 +86,10 @@ public class ZenModePanel extends LinearLayout { private final int mSubheadColor; private final Interpolator mInterpolator; private final int mMaxConditions; + private final int mMaxOptionalConditions; + private final boolean mCountdownConditionSupported; + private final int mFirstConditionIndex; + private final TransitionHelper mTransitionHelper = new TransitionHelper(); private String mTag = TAG + "/" + Integer.toHexString(System.identityHashCode(this)); @@ -98,7 +107,7 @@ public class ZenModePanel extends LinearLayout { private String mExitConditionText; private int mBucketIndex = -1; private boolean mExpanded; - private boolean mHidden = false; + private boolean mHidden; private int mSessionZen; private int mAttachedZen; private boolean mAttached; @@ -117,11 +126,30 @@ public class ZenModePanel extends LinearLayout { mSubheadColor = res.getColor(R.color.qs_subhead); mInterpolator = AnimationUtils.loadInterpolator(mContext, com.android.internal.R.interpolator.fast_out_slow_in); + mCountdownConditionSupported = NotificationManager.from(mContext) + .isSystemConditionProviderEnabled(ZenModeConfig.COUNTDOWN_PATH); + final int countdownDelta = mCountdownConditionSupported ? 1 : 0; + mFirstConditionIndex = COUNTDOWN_CONDITION_INDEX + countdownDelta; + final int minConditions = 1 /*forever*/ + countdownDelta; mMaxConditions = MathUtils.constrain(res.getInteger(R.integer.zen_mode_max_conditions), - 1, 100); + minConditions, 100); + mMaxOptionalConditions = mMaxConditions - minConditions; if (DEBUG) Log.d(mTag, "new ZenModePanel"); } + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("ZenModePanel state:"); + pw.print(" mCountdownConditionSupported="); pw.println(mCountdownConditionSupported); + pw.print(" mMaxConditions="); pw.println(mMaxConditions); + pw.print(" mRequestingConditions="); pw.println(mRequestingConditions); + pw.print(" mAttached="); pw.println(mAttached); + pw.print(" mHidden="); pw.println(mHidden); + pw.print(" mExpanded="); pw.println(mExpanded); + pw.print(" mSessionZen="); pw.println(mSessionZen); + pw.print(" mAttachedZen="); pw.println(mAttachedZen); + mTransitionHelper.dump(fd, pw, args); + } + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -135,6 +163,9 @@ public class ZenModePanel extends LinearLayout { Global.ZEN_MODE_OFF); mZenButtons.setCallback(mZenButtonsCallback); + final ViewGroup zenButtonsContainer = (ViewGroup) findViewById(R.id.zen_buttons_container); + zenButtonsContainer.setLayoutTransition(newLayoutTransition(null)); + mZenSubhead = findViewById(R.id.zen_subhead); mZenSubheadCollapsed = (TextView) findViewById(R.id.zen_subhead_collapsed); @@ -159,15 +190,22 @@ public class ZenModePanel extends LinearLayout { Interaction.register(mMoreSettings, mInteractionCallback); mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions); - setLayoutTransition(newLayoutTransition()); + for (int i = 0; i < mMaxConditions; i++) { + mZenConditions.addView(mInflater.inflate(R.layout.zen_mode_condition, this, false)); + } + + setLayoutTransition(newLayoutTransition(mTransitionHelper)); } - private LayoutTransition newLayoutTransition() { + private LayoutTransition newLayoutTransition(TransitionListener listener) { final LayoutTransition transition = new LayoutTransition(); transition.disableTransitionType(LayoutTransition.DISAPPEARING); transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); - transition.setInterpolator(LayoutTransition.APPEARING, mInterpolator); + transition.disableTransitionType(LayoutTransition.APPEARING); transition.setInterpolator(LayoutTransition.CHANGE_APPEARING, mInterpolator); + if (listener != null) { + transition.addTransitionListener(listener); + } return transition; } @@ -175,11 +213,11 @@ public class ZenModePanel extends LinearLayout { protected void onAttachedToWindow() { super.onAttachedToWindow(); if (DEBUG) Log.d(mTag, "onAttachedToWindow"); - ((ViewGroup) getParent()).setLayoutTransition(newLayoutTransition()); mAttached = true; mAttachedZen = getSelectedZen(-1); mSessionZen = mAttachedZen; - mSessionExitCondition = copy(mExitCondition); + mTransitionHelper.clear(); + setSessionExitCondition(copy(mExitCondition)); refreshExitConditionText(); updateWidgets(); setRequestingConditions(!mHidden); @@ -193,9 +231,16 @@ public class ZenModePanel extends LinearLayout { mAttached = false; mAttachedZen = -1; mSessionZen = -1; - mSessionExitCondition = null; + setSessionExitCondition(null); setExpanded(false); setRequestingConditions(false); + mTransitionHelper.clear(); + } + + private void setSessionExitCondition(Condition condition) { + if (Objects.equals(condition, mSessionExitCondition)) return; + if (DEBUG) Log.d(mTag, "mSessionExitCondition=" + getConditionId(condition)); + mSessionExitCondition = condition; } public void setHidden(boolean hidden) { @@ -228,12 +273,17 @@ public class ZenModePanel extends LinearLayout { } /** Start or stop requesting relevant zen mode exit conditions */ - private void setRequestingConditions(boolean requesting) { + private void setRequestingConditions(final boolean requesting) { if (mRequestingConditions == requesting) return; if (DEBUG) Log.d(mTag, "setRequestingConditions " + requesting); mRequestingConditions = requesting; if (mController != null) { - mController.requestConditions(mRequestingConditions); + AsyncTask.execute(new Runnable() { + @Override + public void run() { + mController.requestConditions(requesting); + } + }); } if (mRequestingConditions) { mTimeCondition = parseExistingTimeCondition(mExitCondition); @@ -248,7 +298,7 @@ public class ZenModePanel extends LinearLayout { mConditions = null; // reset conditions handleUpdateConditions(); } else { - mZenConditions.removeAllViews(); + hideAllConditions(); } } @@ -259,7 +309,7 @@ public class ZenModePanel extends LinearLayout { mSessionZen = getSelectedZen(-1); handleUpdateZen(mController.getZen()); if (DEBUG) Log.d(mTag, "init mExitCondition=" + mExitCondition); - mZenConditions.removeAllViews(); + hideAllConditions(); mController.addCallback(mZenCallback); } @@ -270,6 +320,7 @@ public class ZenModePanel extends LinearLayout { private void setExitCondition(Condition exitCondition) { if (Objects.equals(mExitCondition, exitCondition)) return; mExitCondition = exitCondition; + if (DEBUG) Log.d(mTag, "mExitCondition=" + getConditionId(mExitCondition)); refreshExitConditionText(); updateWidgets(); } @@ -290,7 +341,7 @@ public class ZenModePanel extends LinearLayout { final String forever = mContext.getString(com.android.internal.R.string.zen_mode_forever); if (mExitCondition == null) { mExitConditionText = forever; - } else if (ZenModeConfig.isValidCountdownConditionId(mExitCondition.id)) { + } else if (isCountdown(mExitCondition)) { final Condition condition = parseExistingTimeCondition(mExitCondition); mExitConditionText = condition != null ? condition.summary : forever; } else { @@ -316,6 +367,24 @@ public class ZenModePanel extends LinearLayout { } mZenButtons.setSelectedValue(zen); updateWidgets(); + handleUpdateConditions(); + if (mExpanded) { + final Condition selected = getSelectedCondition(); + if (!Objects.equals(mExitCondition, selected)) { + select(selected); + } + } + } + + private Condition getSelectedCondition() { + final int N = getVisibleConditions(); + for (int i = 0; i < N; i++) { + final ConditionTag tag = getConditionTagAt(i); + if (tag != null && tag.rb.isChecked()) { + return tag.condition; + } + } + return null; } private int getSelectedZen(int defValue) { @@ -324,6 +393,10 @@ public class ZenModePanel extends LinearLayout { } private void updateWidgets() { + if (mTransitionHelper.isTransitioning()) { + mTransitionHelper.pendingUpdateWidgets(); + return; + } final int zen = getSelectedZen(Global.ZEN_MODE_OFF); final boolean zenOff = zen == Global.ZEN_MODE_OFF; final boolean zenImportant = zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; @@ -371,7 +444,7 @@ public class ZenModePanel extends LinearLayout { } private Condition[] trimConditions(Condition[] conditions) { - if (conditions == null || conditions.length <= mMaxConditions) { + if (conditions == null || conditions.length <= mMaxOptionalConditions) { // no need to trim return conditions; } @@ -384,33 +457,34 @@ public class ZenModePanel extends LinearLayout { break; } } - final Condition[] rt = Arrays.copyOf(conditions, mMaxConditions); - if (found >= mMaxConditions) { + final Condition[] rt = Arrays.copyOf(conditions, mMaxOptionalConditions); + if (found >= mMaxOptionalConditions) { // found after the first N, promote to the end of the first N - rt[mMaxConditions - 1] = conditions[found]; + rt[mMaxOptionalConditions - 1] = conditions[found]; } return rt; } private void handleUpdateConditions() { + if (mTransitionHelper.isTransitioning()) { + mTransitionHelper.pendingUpdateConditions(); + return; + } final int conditionCount = mConditions == null ? 0 : mConditions.length; if (DEBUG) Log.d(mTag, "handleUpdateConditions conditionCount=" + conditionCount); - for (int i = mZenConditions.getChildCount() - 1; i >= FIRST_CONDITION_INDEX; i--) { - mZenConditions.removeViewAt(i); - } // forever bind(null, mZenConditions.getChildAt(FOREVER_CONDITION_INDEX)); // countdown - bind(mTimeCondition, mZenConditions.getChildAt(TIME_CONDITION_INDEX)); + if (mCountdownConditionSupported) { + bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX)); + } // provider conditions - boolean foundDowntime = false; for (int i = 0; i < conditionCount; i++) { - bind(mConditions[i], mZenConditions.getChildAt(FIRST_CONDITION_INDEX + i)); - foundDowntime |= isDowntime(mConditions[i]); + bind(mConditions[i], mZenConditions.getChildAt(mFirstConditionIndex + i)); } - // ensure downtime exists, if active - if (isDowntime(mSessionExitCondition) && !foundDowntime) { - bind(mSessionExitCondition, null); + // hide the rest + for (int i = mZenConditions.getChildCount() - 1; i > mFirstConditionIndex + conditionCount; i--) { + mZenConditions.getChildAt(i).setVisibility(GONE); } // ensure something is selected if (mExpanded) { @@ -418,78 +492,101 @@ public class ZenModePanel extends LinearLayout { } } - private static boolean isDowntime(Condition c) { - return ZenModeConfig.isValidDowntimeConditionId(getConditionId(c)); - } - private ConditionTag getConditionTagAt(int index) { return (ConditionTag) mZenConditions.getChildAt(index).getTag(); } + private int getVisibleConditions() { + int rt = 0; + final int N = mZenConditions.getChildCount(); + for (int i = 0; i < N; i++) { + rt += mZenConditions.getChildAt(i).getVisibility() == VISIBLE ? 1 : 0; + } + return rt; + } + + private void hideAllConditions() { + final int N = mZenConditions.getChildCount(); + for (int i = 0; i < N; i++) { + mZenConditions.getChildAt(i).setVisibility(GONE); + } + } + private void ensureSelection() { // are we left without anything selected? if so, set a default - if (mZenConditions.getChildCount() == 0) return; - for (int i = 0; i < mZenConditions.getChildCount(); i++) { - if (getConditionTagAt(i).rb.isChecked()) { - if (DEBUG) Log.d(mTag, "Not selecting a default, checked=" - + getConditionTagAt(i).condition); + final int visibleConditions = getVisibleConditions(); + if (visibleConditions == 0) return; + for (int i = 0; i < visibleConditions; i++) { + final ConditionTag tag = getConditionTagAt(i); + if (tag != null && tag.rb.isChecked()) { + if (DEBUG) Log.d(mTag, "Not selecting a default, checked=" + tag.condition); return; } } + final ConditionTag foreverTag = getConditionTagAt(FOREVER_CONDITION_INDEX); + if (foreverTag == null) return; if (DEBUG) Log.d(mTag, "Selecting a default"); final int favoriteIndex = mPrefs.getMinuteIndex(); - if (favoriteIndex == -1) { - getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true); + if (favoriteIndex == -1 || !mCountdownConditionSupported) { + foreverTag.rb.setChecked(true); } else { mTimeCondition = ZenModeConfig.toTimeCondition(mContext, MINUTE_BUCKETS[favoriteIndex], ActivityManager.getCurrentUser()); mBucketIndex = favoriteIndex; - bind(mTimeCondition, mZenConditions.getChildAt(TIME_CONDITION_INDEX)); - getConditionTagAt(TIME_CONDITION_INDEX).rb.setChecked(true); + bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX)); + getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true); } } private void handleExitConditionChanged(Condition exitCondition) { setExitCondition(exitCondition); if (DEBUG) Log.d(mTag, "handleExitConditionChanged " + mExitCondition); - final int N = mZenConditions.getChildCount(); + final int N = getVisibleConditions(); for (int i = 0; i < N; i++) { final ConditionTag tag = getConditionTagAt(i); - tag.rb.setChecked(sameConditionId(tag.condition, mExitCondition)); + if (tag != null) { + if (sameConditionId(tag.condition, mExitCondition)) { + bind(exitCondition, mZenConditions.getChildAt(i)); + } + } } } - private void bind(final Condition condition, View convertView) { + private boolean isCountdown(Condition c) { + return c != null && ZenModeConfig.isValidCountdownConditionId(c.id); + } + + private void bind(final Condition condition, final View row) { final boolean enabled = condition == null || condition.state == Condition.STATE_TRUE; - final View row; - if (convertView == null) { - row = mInflater.inflate(R.layout.zen_mode_condition, this, false); - if (DEBUG) Log.d(mTag, "Adding new condition view for: " + condition); - mZenConditions.addView(row); - } else { - row = convertView; - } final ConditionTag tag = row.getTag() != null ? (ConditionTag) row.getTag() : new ConditionTag(); row.setTag(tag); + final boolean first = tag.rb == null; if (tag.rb == null) { tag.rb = (RadioButton) row.findViewById(android.R.id.checkbox); } tag.condition = condition; + final Uri conditionId = getConditionId(tag.condition); + if (DEBUG) Log.d(mTag, "bind i=" + mZenConditions.indexOfChild(row) + " first=" + first + + " condition=" + conditionId); tag.rb.setEnabled(enabled); - if ((mSessionExitCondition != null || mAttachedZen != Global.ZEN_MODE_OFF) - && sameConditionId(mSessionExitCondition, tag.condition)) { - tag.rb.setChecked(true); + final boolean checked = (mSessionExitCondition != null + || mAttachedZen != Global.ZEN_MODE_OFF) + && (sameConditionId(mSessionExitCondition, tag.condition) + || isCountdown(mSessionExitCondition) && isCountdown(tag.condition)); + if (checked != tag.rb.isChecked()) { + if (DEBUG) Log.d(mTag, "bind checked=" + checked + " condition=" + conditionId); + tag.rb.setChecked(checked); } tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (mExpanded && isChecked) { - if (DEBUG) Log.d(mTag, "onCheckedChanged " + tag.condition); - final int N = mZenConditions.getChildCount(); + if (DEBUG) Log.d(mTag, "onCheckedChanged " + conditionId); + final int N = getVisibleConditions(); for (int i = 0; i < N; i++) { - ConditionTag childTag = getConditionTagAt(i); - if (childTag == tag) continue; + final ConditionTag childTag = getConditionTagAt(i); + if (childTag == null || childTag == tag) continue; childTag.rb.setChecked(false); } select(tag.condition); @@ -547,8 +644,10 @@ public class ZenModePanel extends LinearLayout { } }); - final long time = ZenModeConfig.tryParseCountdownConditionId(getConditionId(tag.condition)); + final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId); if (time > 0) { + button1.setVisibility(VISIBLE); + button2.setVisibility(VISIBLE); if (mBucketIndex > -1) { button1.setEnabled(mBucketIndex > 0); button2.setEnabled(mBucketIndex < MINUTE_BUCKETS.length - 1); @@ -563,16 +662,17 @@ public class ZenModePanel extends LinearLayout { button1.setAlpha(button1.isEnabled() ? 1f : .5f); button2.setAlpha(button2.isEnabled() ? 1f : .5f); } else { - button1.setVisibility(View.GONE); - button2.setVisibility(View.GONE); + button1.setVisibility(GONE); + button2.setVisibility(GONE); } // wire up interaction callbacks for newly-added condition rows - if (convertView == null) { + if (first) { Interaction.register(tag.rb, mInteractionCallback); Interaction.register(tag.lines, mInteractionCallback); Interaction.register(button1, mInteractionCallback); Interaction.register(button2, mInteractionCallback); } + row.setVisibility(VISIBLE); } private void announceConditionSelection(ConditionTag tag) { @@ -629,18 +729,23 @@ public class ZenModePanel extends LinearLayout { announceConditionSelection(tag); } - private void select(Condition condition) { + private void select(final Condition condition) { if (DEBUG) Log.d(mTag, "select " + condition); if (mController != null) { - mController.setExitCondition(condition); + AsyncTask.execute(new Runnable() { + @Override + public void run() { + mController.setExitCondition(condition); + } + }); } setExitCondition(condition); if (condition == null) { mPrefs.setMinuteIndex(-1); - } else if (ZenModeConfig.isValidCountdownConditionId(condition.id) && mBucketIndex != -1) { + } else if (isCountdown(condition) && mBucketIndex != -1) { mPrefs.setMinuteIndex(mBucketIndex); } - mSessionExitCondition = copy(condition); + setSessionExitCondition(copy(condition)); } private void fireMoreSettings() { @@ -784,10 +889,15 @@ public class ZenModePanel extends LinearLayout { private final SegmentedButtons.Callback mZenButtonsCallback = new SegmentedButtons.Callback() { @Override - public void onSelected(Object value) { + public void onSelected(final Object value) { if (value != null && mZenButtons.isShown()) { if (DEBUG) Log.d(mTag, "mZenButtonsCallback selected=" + value); - mController.setZen((Integer) value); + AsyncTask.execute(new Runnable() { + @Override + public void run() { + mController.setZen((Integer) value); + } + }); } } @@ -803,4 +913,79 @@ public class ZenModePanel extends LinearLayout { fireInteraction(); } }; + + private final class TransitionHelper implements TransitionListener, Runnable { + private final ArraySet<View> mTransitioningViews = new ArraySet<View>(); + + private boolean mTransitioning; + private boolean mPendingUpdateConditions; + private boolean mPendingUpdateWidgets; + + public void clear() { + mTransitioningViews.clear(); + mPendingUpdateConditions = mPendingUpdateWidgets = false; + } + + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println(" TransitionHelper state:"); + pw.print(" mPendingUpdateConditions="); pw.println(mPendingUpdateConditions); + pw.print(" mPendingUpdateWidgets="); pw.println(mPendingUpdateWidgets); + pw.print(" mTransitioning="); pw.println(mTransitioning); + pw.print(" mTransitioningViews="); pw.println(mTransitioningViews); + } + + public void pendingUpdateConditions() { + mPendingUpdateConditions = true; + } + + public void pendingUpdateWidgets() { + mPendingUpdateWidgets = true; + } + + public boolean isTransitioning() { + return !mTransitioningViews.isEmpty(); + } + + @Override + public void startTransition(LayoutTransition transition, + ViewGroup container, View view, int transitionType) { + mTransitioningViews.add(view); + updateTransitioning(); + } + + @Override + public void endTransition(LayoutTransition transition, + ViewGroup container, View view, int transitionType) { + mTransitioningViews.remove(view); + updateTransitioning(); + } + + @Override + public void run() { + if (DEBUG) Log.d(mTag, "TransitionHelper run" + + " mPendingUpdateWidgets=" + mPendingUpdateWidgets + + " mPendingUpdateConditions=" + mPendingUpdateConditions); + if (mPendingUpdateWidgets) { + updateWidgets(); + } + if (mPendingUpdateConditions) { + handleUpdateConditions(); + } + mPendingUpdateWidgets = mPendingUpdateConditions = false; + } + + private void updateTransitioning() { + final boolean transitioning = isTransitioning(); + if (mTransitioning == transitioning) return; + mTransitioning = transitioning; + if (DEBUG) Log.d(mTag, "TransitionHelper mTransitioning=" + mTransitioning); + if (!mTransitioning) { + if (mPendingUpdateConditions || mPendingUpdateWidgets) { + mHandler.post(this); + } else { + mPendingUpdateConditions = mPendingUpdateWidgets = false; + } + } + } + } } |
