Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1c0ca508 authored by Adrian Roos's avatar Adrian Roos
Browse files

Continue inline reply prototype

Adds IME focus management, prevents HUNs with active
RemoteInput from disappearing and fixes a few issues
with adding RemoteInputs from WearableExtenders.

Bug: 22452379
Change-Id: Ie3bd8e5d4635bd1e967afe2b894ad617dbc4ed10
parent e090a1e0
Loading
Loading
Loading
Loading
+22 −6
Original line number Diff line number Diff line
@@ -1539,6 +1539,7 @@ public abstract class BaseStatusBar extends SystemUI implements
            if (viableAction != null) {
                Notification stripped = n.clone();
                Notification.Builder.stripForDelivery(stripped);
                stripped.extras.putBoolean("android.rebuild", true);
                stripped.actions = new Notification.Action[] { viableAction };
                stripped.extras.putBoolean("android.rebuild.contentView", true);
                stripped.contentView = null;
@@ -1547,6 +1548,13 @@ public abstract class BaseStatusBar extends SystemUI implements
                stripped.extras.putBoolean("android.rebuild.hudView", true);
                stripped.headsUpContentView = null;

                stripped.extras.putParcelable(Notification.EXTRA_LARGE_ICON,
                        stripped.getLargeIcon());
                if (SystemProperties.getBoolean("debug.strip_third_line", false)) {
                    stripped.extras.putCharSequence(Notification.EXTRA_INFO_TEXT, null);
                    stripped.extras.putCharSequence(Notification.EXTRA_SUMMARY_TEXT, null);
                }

                Notification rebuilt = Notification.Builder.rebuild(mContext, stripped);

                n.actions = rebuilt.actions;
@@ -1580,27 +1588,35 @@ public abstract class BaseStatusBar extends SystemUI implements
        if (remoteInput != null) {
            View bigContentView = entry.getExpandedContentView();
            if (bigContentView != null) {
                inflateRemoteInput(bigContentView, remoteInput, actions);
                inflateRemoteInput(bigContentView, entry, remoteInput, actions);
            }
            View headsUpContentView = entry.getHeadsUpContentView();
            if (headsUpContentView != null) {
                inflateRemoteInput(headsUpContentView, remoteInput, actions);
                inflateRemoteInput(headsUpContentView, entry, remoteInput, actions);
            }
        }

    }

    private void inflateRemoteInput(View view, RemoteInput remoteInput,
    private void inflateRemoteInput(View view, Entry entry, RemoteInput remoteInput,
            Notification.Action[] actions) {
        View actionContainerCandidate = view.findViewById(com.android.internal.R.id.actions);
        if (actionContainerCandidate instanceof ViewGroup) {
            ViewGroup actionContainer = (ViewGroup) actionContainerCandidate;
            RemoteInputView riv = inflateRemoteInputView(actionContainer, entry,
                    actions[0], remoteInput);
            if (riv != null) {
                actionContainer.removeAllViews();
            actionContainer.addView(
                    RemoteInputView.inflate(mContext, actionContainer, actions[0], remoteInput));
                actionContainer.addView(riv);
            }
        }
    }

    protected RemoteInputView inflateRemoteInputView(ViewGroup root, Entry entry,
            Notification.Action action, RemoteInput remoteInput) {
        return null;
    }

    private final class NotificationClicker implements View.OnClickListener {
        public void onClick(final View v) {
            if (!(v instanceof ExpandableNotificationRow)) {
+102 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.systemui.statusbar;

import com.android.internal.util.Preconditions;
import com.android.systemui.statusbar.phone.StatusBarWindowManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.RemoteInputView;

import java.lang.ref.WeakReference;
import java.util.ArrayList;

/**
 * Keeps track of the currently active {@link RemoteInputView}s.
 */
public class RemoteInputController {

    private final ArrayList<WeakReference<NotificationData.Entry>> mRemoteInputs = new ArrayList<>();
    private final StatusBarWindowManager mStatusBarWindowManager;
    private final HeadsUpManager mHeadsUpManager;

    public RemoteInputController(StatusBarWindowManager sbwm, HeadsUpManager headsUpManager) {
        mStatusBarWindowManager = sbwm;
        mHeadsUpManager = headsUpManager;
    }

    public void addRemoteInput(NotificationData.Entry entry) {
        Preconditions.checkNotNull(entry);

        boolean found = pruneWeakThenRemoveAndContains(
                entry /* contains */, null /* remove */);
        if (!found) {
            mRemoteInputs.add(new WeakReference<>(entry));
        }

        apply(entry);
    }

    public void removeRemoteInput(NotificationData.Entry entry) {
        Preconditions.checkNotNull(entry);

        pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */);

        apply(entry);
    }

    private void apply(NotificationData.Entry entry) {
        mStatusBarWindowManager.setRemoteInputActive(isRemoteInputActive());
        mHeadsUpManager.setRemoteInputActive(entry, isRemoteInputActive(entry));
    }

    /**
     * @return true if {@param entry} has an active RemoteInput
     */
    public boolean isRemoteInputActive(NotificationData.Entry entry) {
        return pruneWeakThenRemoveAndContains(entry /* contains */, null /* remove */);
    }

    /**
     * @return true if any entry has an active RemoteInput
     */
    public boolean isRemoteInputActive() {
        pruneWeakThenRemoveAndContains(null /* contains */, null /* remove */);
        return !mRemoteInputs.isEmpty();
    }

    /**
     * Prunes dangling weak references, removes entries referring to {@param remove} and returns
     * whether {@param contains} is part of the array in a single loop.
     * @param remove if non-null, removes this entry from the active remote inputs
     * @return true if {@param contains} is in the set of active remote inputs
     */
    private boolean pruneWeakThenRemoveAndContains(
            NotificationData.Entry contains, NotificationData.Entry remove) {
        boolean found = false;
        for (int i = mRemoteInputs.size() - 1; i >= 0; i--) {
            NotificationData.Entry item = mRemoteInputs.get(i).get();
            if (item == null || item == remove) {
                mRemoteInputs.remove(i);
            } else if (item == contains) {
                found = true;
            }
        }
        return found;
    }


}
+15 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
@@ -82,6 +83,7 @@ import android.view.MotionEvent;
import android.view.ThreadedRenderer;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewStub;
import android.view.WindowManager;
@@ -128,6 +130,7 @@ import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationData.Entry;
import com.android.systemui.statusbar.NotificationOverflowContainer;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.SpeedBumpView;
@@ -150,6 +153,7 @@ import com.android.systemui.statusbar.policy.LocationControllerImpl;
import com.android.systemui.statusbar.policy.NetworkControllerImpl;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.PreviewInflater;
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
import com.android.systemui.statusbar.policy.SecurityControllerImpl;
import com.android.systemui.statusbar.policy.UserInfoController;
@@ -299,6 +303,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,

    StatusBarIconController mIconController;

    private RemoteInputController mRemoteInputController;

    // expanded notifications
    NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
    View mExpandedContents;
@@ -1084,6 +1090,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
        return mStatusBarWindow;
    }

    @Override
    protected RemoteInputView inflateRemoteInputView(ViewGroup root, Entry entry,
            Notification.Action action, RemoteInput remoteInput) {
        return RemoteInputView.inflate(mContext, root, entry, action, remoteInput,
                mRemoteInputController);
    }

    public int getStatusBarHeight() {
        if (mNaturalBarHeight < 0) {
            final Resources res = mContext.getResources();
@@ -2840,6 +2853,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
    private void addStatusBarWindow() {
        makeStatusBarView();
        mStatusBarWindowManager = new StatusBarWindowManager(mContext);
        mRemoteInputController = new RemoteInputController(mStatusBarWindowManager,
                mHeadsUpManager);
        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
    }

+8 −1
Original line number Diff line number Diff line
@@ -123,7 +123,7 @@ public class StatusBarWindowManager {
    private void applyFocusableFlag(State state) {
        boolean panelFocusable = state.statusBarFocusable && state.panelExpanded;
        if (state.keyguardShowing && state.keyguardNeedsInput && state.bouncerShowing
                || BaseStatusBar.ENABLE_REMOTE_INPUT && panelFocusable) {
                || BaseStatusBar.ENABLE_REMOTE_INPUT && state.remoteInputActive) {
            mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
            mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
        } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
@@ -292,6 +292,11 @@ public class StatusBarWindowManager {
        apply(mCurrentState);
    }

    public void setRemoteInputActive(boolean remoteInputActive) {
        mCurrentState.remoteInputActive = remoteInputActive;
        apply(mCurrentState);
    }

    /**
     * Set whether the screen brightness is forced to the value we use for doze mode by the status
     * bar window.
@@ -326,6 +331,8 @@ public class StatusBarWindowManager {
         */
        int statusBarState;

        boolean remoteInputActive;

        private boolean isKeyguardShowingAndNotOccluded() {
            return keyguardShowing && !keyguardOccluded;
        }
+22 −2
Original line number Diff line number Diff line
@@ -102,6 +102,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
    private boolean mHeadsUpGoingAway;
    private boolean mWaitingOnCollapseWhenGoingAway;
    private boolean mIsObserving;
    private boolean mRemoteInputActive;

    public HeadsUpManager(final Context context, View statusBarWindowView) {
        mContext = context;
@@ -536,6 +537,18 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
        return clicked != null && clicked;
    }

    public void setRemoteInputActive(NotificationData.Entry entry, boolean remoteInputActive) {
        HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
        if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
            headsUpEntry.remoteInputActive = remoteInputActive;
            if (remoteInputActive) {
                headsUpEntry.removeAutoRemovalCallbacks();
            } else {
                headsUpEntry.updateEntry(false /* updatePostTime */);
            }
        }
    }

    /**
     * This represents a notification and how long it is in a heads up mode. It also manages its
     * lifecycle automatically when created.
@@ -545,6 +558,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
        public long postTime;
        public long earliestRemovaltime;
        private Runnable mRemoveHeadsUpRunnable;
        public boolean remoteInputActive;

        public void setEntry(final NotificationData.Entry entry) {
            this.entry = entry;
@@ -565,12 +579,18 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
        }

        public void updateEntry() {
            updateEntry(true);
        }

        public void updateEntry(boolean updatePostTime) {
            mSortedEntries.remove(HeadsUpEntry.this);
            long currentTime = mClock.currentTimeMillis();
            earliestRemovaltime = currentTime + mMinimumDisplayTime;
            if (updatePostTime) {
                postTime = Math.max(postTime, currentTime);
            }
            removeAutoRemovalCallbacks();
            if (!hasFullScreenIntent(entry)) {
            if (!hasFullScreenIntent(entry) && !mRemoteInputActive) {
                long finishTime = postTime + mHeadsUpNotificationDecay;
                long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
                mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay);
Loading