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

Commit 78b8c8fb authored by John Spurlock's avatar John Spurlock
Browse files

QS: Wire up screen casting to tile/panel.

Use MediaProjectionManager to determine whether or not
screencasting is active, when it changes, and to stop
casting.

Also:
 - Implement hashCode/equals on MediaProjectionInfo
 - Fix unintentional recursion in the service.

Bug:16488053
Change-Id: Icd1a88f23bbdf1d4c1915b30cb2508f8fe9d6d7e
parent 6adf1588
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;

import java.util.Objects;

/** @hide */
public final class MediaProjectionInfo implements Parcelable {
    private final String mPackageName;
@@ -43,6 +45,21 @@ public final class MediaProjectionInfo implements Parcelable {
        return mUserHandle;
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof MediaProjectionInfo) {
            final MediaProjectionInfo other = (MediaProjectionInfo) o;
            return Objects.equals(other.mPackageName, mPackageName)
                    && Objects.equals(other.mUserHandle, mUserHandle);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return Objects.hash(mPackageName, mUserHandle);
    }

    @Override
    public String toString() {
        return "MediaProjectionInfo{mPackageName="
+2 −1
Original line number Diff line number Diff line
@@ -239,7 +239,8 @@ public class CastTile extends QSTile<QSTile.BooleanState> {
        @Override
        public void onDetailItemDisconnect(Item item) {
            if (item == null || item.tag == null) return;
            mController.stopCasting();
            final CastDevice device = (CastDevice) item.tag;
            mController.stopCasting(device);
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@ public interface CastController {
    void setCurrentUserId(int currentUserId);
    Set<CastDevice> getCastDevices();
    void startCasting(CastDevice device);
    void stopCasting();
    void stopCasting(CastDevice device);

    public interface Callback {
        void onCastDevicesChanged();
+92 −3
Original line number Diff line number Diff line
@@ -19,15 +19,25 @@ package com.android.systemui.statusbar.policy;
import static android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
import android.os.Handler;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;

import com.android.systemui.R;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;

@@ -41,12 +51,19 @@ public class CastControllerImpl implements CastController {
    private final MediaRouter mMediaRouter;
    private final ArrayMap<String, RouteInfo> mRoutes = new ArrayMap<>();
    private final Object mDiscoveringLock = new Object();
    private final MediaProjectionManager mProjectionManager;
    private final Object mProjectionLock = new Object();

    private boolean mDiscovering;
    private MediaProjectionInfo mProjection;

    public CastControllerImpl(Context context) {
        mContext = context;
        mMediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
        mProjectionManager = (MediaProjectionManager)
                context.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
        mProjection = mProjectionManager.getActiveProjectionInfo();
        mProjectionManager.addCallback(mProjectionCallback, new Handler());
        if (DEBUG) Log.d(TAG, "new CastController()");
    }

@@ -59,6 +76,7 @@ public class CastControllerImpl implements CastController {
            final RouteInfo route = mRoutes.valueAt(i);
            pw.print("    "); pw.println(routeToString(route));
        }
        pw.print("  mProjection="); pw.println(mProjection);
    }

    @Override
@@ -95,6 +113,18 @@ public class CastControllerImpl implements CastController {
    @Override
    public Set<CastDevice> getCastDevices() {
        final ArraySet<CastDevice> devices = new ArraySet<CastDevice>();
        synchronized (mProjectionLock) {
            if (mProjection != null) {
                final CastDevice device = new CastDevice();
                device.id = mProjection.getPackageName();
                device.name = getAppName(mProjection.getPackageName());
                device.description = mContext.getString(R.string.quick_settings_casting);
                device.state = CastDevice.STATE_CONNECTED;
                device.tag = mProjection;
                devices.add(device);
                return devices;
            }
        }
        synchronized(mRoutes) {
            for (RouteInfo route : mRoutes.values()) {
                final CastDevice device = new CastDevice();
@@ -122,10 +152,56 @@ public class CastControllerImpl implements CastController {
    }

    @Override
    public void stopCasting() {
        if (DEBUG) Log.d(TAG, "stopCasting");
    public void stopCasting(CastDevice device) {
        final boolean isProjection = device.tag instanceof MediaProjectionInfo;
        if (DEBUG) Log.d(TAG, "stopCasting isProjection=" + isProjection);
        if (isProjection) {
            final MediaProjectionInfo projection = (MediaProjectionInfo) device.tag;
            if (Objects.equals(mProjectionManager.getActiveProjectionInfo(), projection)) {
                mProjectionManager.stopActiveProjection();
            } else {
                Log.w(TAG, "Projection is no longer active: " + projection);
            }
        } else {
            mMediaRouter.getDefaultRoute().select();
        }
    }

    private void setProjection(MediaProjectionInfo projection, boolean started) {
        boolean changed = false;
        final MediaProjectionInfo oldProjection = mProjection;
        synchronized (mProjectionLock) {
            final boolean isCurrent = Objects.equals(projection, mProjection);
            if (started && !isCurrent) {
                mProjection = projection;
                changed = true;
            } else if (!started && isCurrent) {
                mProjection = null;
                changed = true;
            }
        }
        if (changed) {
            if (DEBUG) Log.d(TAG, "setProjection: " + oldProjection + " -> " + mProjection);
            fireOnCastDevicesChanged();
        }
    }

    private String getAppName(String packageName) {
        final PackageManager pm = mContext.getPackageManager();
        try {
            final ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0);
            if (appInfo != null) {
                final CharSequence label = appInfo.loadLabel(pm);
                if (!TextUtils.isEmpty(label)) {
                    return label.toString();
                }
            }
            Log.w(TAG, "No label found for package: " + packageName);
        } catch (NameNotFoundException e) {
            Log.w(TAG, "Error getting appName for package: " + packageName, e);
        }
        return packageName;
    }

    private void updateRemoteDisplays() {
        synchronized(mRoutes) {
@@ -202,4 +278,17 @@ public class CastControllerImpl implements CastController {
            updateRemoteDisplays();
        }
    };

    private final MediaProjectionManager.Callback mProjectionCallback
            = new MediaProjectionManager.Callback() {
        @Override
        public void onStart(MediaProjectionInfo info) {
            setProjection(info, true);
        }

        @Override
        public void onStop(MediaProjectionInfo info) {
            setProjection(info, false);
        }
    };
}
+1 −2
Original line number Diff line number Diff line
@@ -111,7 +111,6 @@ public final class MediaProjectionManagerService extends SystemService
            @Override
            public void binderDied() {
                synchronized (mLock) {
                    unlinkDeathRecipientLocked(callback);
                    removeCallback(callback);
                }
            }
@@ -125,7 +124,7 @@ public final class MediaProjectionManagerService extends SystemService
    private void removeCallback(IMediaProjectionWatcherCallback callback) {
        synchronized (mLock) {
            unlinkDeathRecipientLocked(callback);
            removeCallback(callback);
            mCallbackDelegate.remove(callback);
        }
    }