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

Commit ab96fe0b authored by Fabian Kozynski's avatar Fabian Kozynski Committed by Android (Google) Code Review
Browse files

Merge "Separates logic from Chip. Chip is notch-aware."

parents 4a08723c 8d06c710
Loading
Loading
Loading
Loading
+8 −3
Original line number Diff line number Diff line
@@ -21,7 +21,8 @@
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_margin="@dimen/ongoing_appops_chip_margin"
    android:gravity="center_vertical|end"
    android:gravity="center_vertical|center_horizontal"
    android:layout_gravity="center_vertical|end"
    android:orientation="horizontal"
    android:paddingStart="@dimen/ongoing_appops_chip_side_padding"
    android:paddingEnd="@dimen/ongoing_appops_chip_side_padding"
@@ -32,13 +33,17 @@
            android:id="@+id/icons_container"
            android:layout_height="match_parent"
            android:layout_width="wrap_content"
            android:gravity="center_vertical|start"
            android:layout_gravity="center_vertical|start"
            android:gravity="center_vertical"
            />

        <TextView
            android:id="@+id/app_name"
            android:layout_height="match_parent"
            android:layout_width="wrap_content"
            android:gravity="center_vertical|end"
            android:singleLine="true"
            android:ellipsize="end"
            android:layout_gravity="center_vertical|end"
            android:gravity="center_vertical"
        />
</com.android.systemui.privacy.OngoingPrivacyChip>
 No newline at end of file
+17 −1
Original line number Diff line number Diff line
@@ -27,6 +27,13 @@
    android:paddingStart="@dimen/status_bar_padding_start"
    android:paddingEnd="@dimen/status_bar_padding_end" >

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="horizontal"
        android:gravity="center_vertical|start" >

    <com.android.systemui.statusbar.policy.Clock
        android:id="@+id/clock"
        android:layout_width="wrap_content"
@@ -38,13 +45,21 @@
        android:singleLine="true"
        android:textAppearance="@style/TextAppearance.StatusBar.Clock"
        systemui:showDark="false" />
    </LinearLayout>

    <android.widget.Space
        android:id="@+id/space"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_gravity="center_vertical|center_horizontal"
        android:visibility="gone" />

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:gravity="center_vertical|center_horizontal" />
        android:orientation="horizontal"
        android:gravity="center_vertical|end">

    <include layout="@layout/ongoing_privacy_chip" />

@@ -54,3 +69,4 @@
        android:layout_width="wrap_content"
        android:gravity="center_vertical|end" />
    </LinearLayout>
</LinearLayout>
+6 −67
Original line number Diff line number Diff line
@@ -14,21 +14,14 @@

package com.android.systemui.privacy

import android.app.ActivityManager
import android.app.AppOpsManager
import android.content.Context
import android.graphics.Color
import android.os.UserHandle
import android.os.UserManager
import android.util.AttributeSet
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import com.android.systemui.Dependency
import com.android.systemui.R
import com.android.systemui.appops.AppOpItem
import com.android.systemui.appops.AppOpsController

class OngoingPrivacyChip @JvmOverloads constructor(
    context: Context,
@@ -37,36 +30,14 @@ class OngoingPrivacyChip @JvmOverloads constructor(
    defStyleRes: Int = 0
) : LinearLayout(context, attrs, defStyleAttrs, defStyleRes) {

    companion object {
        val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
                AppOpsManager.OP_RECORD_AUDIO,
                AppOpsManager.OP_COARSE_LOCATION,
                AppOpsManager.OP_FINE_LOCATION)
    }

    private lateinit var appName: TextView
    private lateinit var iconsContainer: LinearLayout
    private var privacyList = emptyList<PrivacyItem>()
    private val appOpsController = Dependency.get(AppOpsController::class.java)
    private val userManager = context.getSystemService(UserManager::class.java)
    private val currentUser = ActivityManager.getCurrentUser()
    private val currentUserIds = userManager.getProfiles(currentUser).map { it.id }
    private var listening = false

    var builder = PrivacyDialogBuilder(context, privacyList)

    private val callback = object : AppOpsController.Callback {
        override fun onActiveStateChanged(
            code: Int,
            uid: Int,
            packageName: String,
            active: Boolean
        ) {
            val userId = UserHandle.getUserId(uid)
            if (userId in currentUserIds) {
                updatePrivacyList()
            }
        }
    var builder = PrivacyDialogBuilder(context, emptyList<PrivacyItem>())
    var privacyList = emptyList<PrivacyItem>()
        set(value) {
            field = value
            builder = PrivacyDialogBuilder(context, value)
            updateView()
        }

    override fun onFinishInflate() {
@@ -76,36 +47,6 @@ class OngoingPrivacyChip @JvmOverloads constructor(
        iconsContainer = findViewById(R.id.icons_container)
    }

    fun setListening(listen: Boolean) {
        if (listening == listen) return
        listening = listen
        if (listening) {
            appOpsController.addCallback(OPS, callback)
            updatePrivacyList()
        } else {
            appOpsController.removeCallback(OPS, callback)
        }
    }

    private fun updatePrivacyList() {
        privacyList = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
                .mapNotNull { toPrivacyItem(it) }
        builder = PrivacyDialogBuilder(context, privacyList)
        updateView()
    }

    private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
        val type: PrivacyType = when (appOpItem.code) {
            AppOpsManager.OP_CAMERA -> PrivacyType.TYPE_CAMERA
            AppOpsManager.OP_COARSE_LOCATION -> PrivacyType.TYPE_LOCATION
            AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION
            AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
            else -> return null
        }
        val app = PrivacyApplication(appOpItem.packageName, context)
        return PrivacyItem(type, app, appOpItem.timeStarted)
    }

    // Should only be called if the builder icons or app changed
    private fun updateView() {
        fun setIcons(dialogBuilder: PrivacyDialogBuilder, iconsContainer: ViewGroup) {
@@ -121,11 +62,9 @@ class OngoingPrivacyChip @JvmOverloads constructor(
        }

        if (privacyList.isEmpty()) {
            visibility = GONE
            return
        } else {
            generateContentDescription()
            visibility = VISIBLE
            setIcons(builder, iconsContainer)
            appName.visibility = GONE
            builder.app?.let {
+105 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.privacy

import android.app.ActivityManager
import android.app.AppOpsManager
import android.content.Context
import android.os.Handler
import android.os.UserHandle
import android.os.UserManager
import com.android.systemui.Dependency
import com.android.systemui.appops.AppOpItem
import com.android.systemui.appops.AppOpsController

class PrivacyItemController(val context: Context, val callback: Callback) {

    companion object {
        val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
                AppOpsManager.OP_RECORD_AUDIO,
                AppOpsManager.OP_COARSE_LOCATION,
                AppOpsManager.OP_FINE_LOCATION)
    }

    private var privacyList = emptyList<PrivacyItem>()
    private val appOpsController = Dependency.get(AppOpsController::class.java)
    private val userManager = context.getSystemService(UserManager::class.java)
    private val currentUser = ActivityManager.getCurrentUser()
    private val currentUserIds = userManager.getProfiles(currentUser).map { it.id }
    private val bgHandler = Handler(Dependency.get(Dependency.BG_LOOPER))
    private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER)
    private val notifyChanges = Runnable {
        callback.privacyChanged(privacyList)
    }
    private val updateListAndNotifyChanges = Runnable {
        updatePrivacyList()
        uiHandler.post(notifyChanges)
    }

    private var listening = false

    private val cb = object : AppOpsController.Callback {
        override fun onActiveStateChanged(
            code: Int,
            uid: Int,
            packageName: String,
            active: Boolean
        ) {
            val userId = UserHandle.getUserId(uid)
            if (userId in currentUserIds) {
                update()
            }
        }
    }

    private fun update() {
        bgHandler.post(updateListAndNotifyChanges)
    }

    fun setListening(listen: Boolean) {
        if (listening == listen) return
        listening = listen
        if (listening) {
            appOpsController.addCallback(OPS, cb)
            update()
        } else {
            appOpsController.removeCallback(OPS, cb)
        }
    }

    private fun updatePrivacyList() {
        privacyList = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
                .mapNotNull { toPrivacyItem(it) }
    }

    private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
        val type: PrivacyType = when (appOpItem.code) {
            AppOpsManager.OP_CAMERA -> PrivacyType.TYPE_CAMERA
            AppOpsManager.OP_COARSE_LOCATION -> PrivacyType.TYPE_LOCATION
            AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION
            AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
            else -> return null
        }
        val app = PrivacyApplication(appOpItem.packageName, context)
        return PrivacyItem(type, app, appOpItem.timeStarted)
    }

    // Used by containing class to get notified of changes
    interface Callback {
        fun privacyChanged(privacyItems: List<PrivacyItem>)
    }
}
 No newline at end of file
+48 −2
Original line number Diff line number Diff line
@@ -39,12 +39,15 @@ import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.view.DisplayCutout;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Space;
import android.widget.TextView;

import androidx.annotation.VisibleForTesting;
@@ -57,6 +60,8 @@ import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.privacy.OngoingPrivacyDialog;
import com.android.systemui.privacy.PrivacyItem;
import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.qs.QSDetail.Callback;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -70,6 +75,7 @@ import com.android.systemui.statusbar.policy.DateView;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.ZenModeController;

import java.util.List;
import java.util.Locale;
import java.util.Objects;

@@ -125,9 +131,11 @@ public class QuickStatusBarHeader extends RelativeLayout implements
    private Clock mClockView;
    private DateView mDateView;
    private OngoingPrivacyChip mPrivacyChip;
    private Space mSpace;

    private NextAlarmController mAlarmController;
    private ZenModeController mZenController;
    private PrivacyItemController mPrivacyItemController;
    /** Counts how many times the long press tooltip has been shown to the user. */
    private int mShownCount;

@@ -138,16 +146,26 @@ public class QuickStatusBarHeader extends RelativeLayout implements
            updateStatusText();
        }
    };
    private boolean mHasTopCutout = false;

    /**
     * Runnable for automatically fading out the long press tooltip (as if it were animating away).
     */
    private final Runnable mAutoFadeOutTooltipRunnable = () -> hideLongPressTooltip(false);

    private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
        @Override
        public void privacyChanged(List<PrivacyItem> privacyItems) {
            mPrivacyChip.setPrivacyList(privacyItems);
            setChipVisibility(!privacyItems.isEmpty());
        }
    };

    public QuickStatusBarHeader(Context context, AttributeSet attrs) {
        super(context, attrs);
        mAlarmController = Dependency.get(NextAlarmController.class);
        mZenController = Dependency.get(ZenModeController.class);
        mPrivacyItemController = new PrivacyItemController(context, mPICCallback);
        mShownCount = getStoredShownCount();
    }

@@ -194,6 +212,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
        mDateView = findViewById(R.id.date);
        mPrivacyChip = findViewById(R.id.privacy_chip);
        mPrivacyChip.setOnClickListener(this);
        mSpace = findViewById(R.id.space);
    }

    private void updateStatusText() {
@@ -208,6 +227,16 @@ public class QuickStatusBarHeader extends RelativeLayout implements
        }
    }

    private void setChipVisibility(boolean chipVisible) {
        mBatteryMeterView.setVisibility(View.VISIBLE);
        if (chipVisible) {
            mPrivacyChip.setVisibility(View.VISIBLE);
            if (mHasTopCutout) mBatteryMeterView.setVisibility(View.GONE);
        } else {
            mPrivacyChip.setVisibility(View.GONE);
        }
    }

    private boolean updateRingerStatus() {
        boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
        CharSequence originalRingerText = mRingerModeTextView.getText();
@@ -411,8 +440,9 @@ public class QuickStatusBarHeader extends RelativeLayout implements

    @Override
    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
        DisplayCutout cutout = insets.getDisplayCutout();
        Pair<Integer, Integer> padding = PhoneStatusBarView.cornerCutoutMargins(
                insets.getDisplayCutout(), getDisplay());
                cutout, getDisplay());
        if (padding == null) {
            mSystemIconsView.setPaddingRelative(
                    getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start), 0,
@@ -421,6 +451,22 @@ public class QuickStatusBarHeader extends RelativeLayout implements
            mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);

        }
        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams();
        if (cutout != null) {
            Rect topCutout = cutout.getBoundingRectTop();
            if (topCutout.isEmpty()) {
                mHasTopCutout = false;
                lp.width = 0;
                mSpace.setVisibility(View.GONE);
            } else {
                mHasTopCutout = true;
                lp.width = topCutout.width();
                mSpace.setVisibility(View.VISIBLE);
            }
        }
        mSpace.setLayoutParams(lp);
        // Decide whether to show BatteryMeterView
        setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
        return super.onApplyWindowInsets(insets);
    }

@@ -437,7 +483,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
            return;
        }
        mHeaderQsPanel.setListening(listening);
        mPrivacyChip.setListening(listening);
        mPrivacyItemController.setListening(listening);
        mListening = listening;

        if (listening) {
Loading