Merge "Adding the privacy chip to the CarStatusBar" into qt-dev
am: 09c5edd7a0
Change-Id: I5d8bfd7e84f7c771389e0ab9049d2ef70247c513
This commit is contained in:
37
packages/CarSystemUI/res/layout/car_ongoing_privacy_chip.xml
Normal file
37
packages/CarSystemUI/res/layout/car_ongoing_privacy_chip.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2019 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.
|
||||
-->
|
||||
|
||||
<com.android.systemui.statusbar.car.privacy.OngoingPrivacyChip
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/car_privacy_chip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="@dimen/ongoing_appops_chip_margin"
|
||||
android:layout_toStartOf="@+id/clock_container"
|
||||
android:focusable="true"
|
||||
android:gravity="center_vertical|end"
|
||||
android:orientation="horizontal"
|
||||
android:paddingEnd="@dimen/ongoing_appops_chip_side_padding"
|
||||
android:paddingStart="@dimen/ongoing_appops_chip_side_padding"
|
||||
android:visibility="visible">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/icons_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical|start"/>
|
||||
</com.android.systemui.statusbar.car.privacy.OngoingPrivacyChip>
|
||||
@@ -65,6 +65,8 @@
|
||||
/>
|
||||
</FrameLayout>
|
||||
|
||||
<include layout="@layout/car_ongoing_privacy_chip"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/clock_container"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -59,6 +59,16 @@
|
||||
<dimen name="car_keyline_1">24dp</dimen>
|
||||
<dimen name="car_keyline_2">96dp</dimen>
|
||||
<dimen name="car_keyline_3">128dp</dimen>
|
||||
<dimen name="privacy_chip_icon_height">36dp</dimen>
|
||||
<dimen name="privacy_chip_icon_padding_left">0dp</dimen>
|
||||
<dimen name="privacy_chip_icon_padding_right">0dp</dimen>
|
||||
<dimen name="privacy_chip_icon_padding_top">0dp</dimen>
|
||||
<dimen name="privacy_chip_icon_padding_bottom">0dp</dimen>
|
||||
|
||||
<dimen name="privacy_chip_text_padding_left">0dp</dimen>
|
||||
<dimen name="privacy_chip_text_padding_right">0dp</dimen>
|
||||
<dimen name="privacy_chip_text_padding_top">0dp</dimen>
|
||||
<dimen name="privacy_chip_text_padding_bottom">0dp</dimen>
|
||||
|
||||
<dimen name="privacy_chip_icon_max_height">100dp</dimen>
|
||||
|
||||
@@ -76,6 +86,16 @@
|
||||
<dimen name="ongoing_appops_chip_bg_padding">4dp</dimen>
|
||||
<!-- Radius of Ongoing App Ops chip corners -->
|
||||
<dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
|
||||
<!-- Start padding for the app icon displayed in the dialog -->
|
||||
<dimen name="privacy_dialog_app_icon_padding_start">40dp</dimen>
|
||||
<!-- End padding for the app opps icon displayed in the dialog -->
|
||||
<dimen name="privacy_dialog_app_ops_icon_padding_end">40dp</dimen>
|
||||
<!-- Top padding for the list of application displayed in the dialog -->
|
||||
<dimen name="privacy_dialog_app_list_padding_top">20dp</dimen>
|
||||
<!-- Top padding for the dialog container-->
|
||||
<dimen name="privacy_dialog_container_padding_top">10dp</dimen>
|
||||
<!-- Top padding for the dialog title-->
|
||||
<dimen name="privacy_dialog_title_padding_start">10dp</dimen>
|
||||
|
||||
<!-- Car volume dimens. -->
|
||||
<dimen name="car_volume_item_height">@*android:dimen/car_single_line_list_item_height</dimen>
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.car.privacy;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.appops.AppOpItem;
|
||||
import com.android.systemui.appops.AppOpsController;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Layout defining the privacy chip that will be displayed in CarStatusRar with the information for
|
||||
* which applications are using AppOpps permission fpr camera, mic and location.
|
||||
*/
|
||||
public class OngoingPrivacyChip extends LinearLayout implements View.OnClickListener {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private LinearLayout mIconsContainer;
|
||||
private List<PrivacyItem> mPrivacyItems;
|
||||
private static AppOpsController sAppOpsController;
|
||||
private UserManager mUserManager;
|
||||
private int mCurrentUser;
|
||||
private List<Integer> mCurrentUserIds;
|
||||
private boolean mListening = false;
|
||||
PrivacyDialogBuilder mPrivacyDialogBuilder;
|
||||
private LinearLayout mPrivacyChip;
|
||||
private ActivityStarter mActivityStarter;
|
||||
|
||||
protected static final int[] OPS = new int[]{
|
||||
AppOpsManager.OP_CAMERA,
|
||||
AppOpsManager.OP_RECORD_AUDIO,
|
||||
AppOpsManager.OP_COARSE_LOCATION,
|
||||
AppOpsManager.OP_FINE_LOCATION
|
||||
};
|
||||
|
||||
public OngoingPrivacyChip(Context context) {
|
||||
super(context, null);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public OngoingPrivacyChip(Context context, AttributeSet attr) {
|
||||
super(context, attr);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public OngoingPrivacyChip(Context context, AttributeSet attr, int defStyle) {
|
||||
super(context, attr, defStyle);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public OngoingPrivacyChip(Context context, AttributeSet attr, int defStyle, int a) {
|
||||
super(context, attr, defStyle, a);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
mContext = context;
|
||||
mPrivacyItems = new ArrayList<>();
|
||||
sAppOpsController = Dependency.get(AppOpsController.class);
|
||||
mUserManager = mContext.getSystemService(UserManager.class);
|
||||
mActivityStarter = Dependency.get(ActivityStarter.class);
|
||||
mCurrentUser = ActivityManager.getCurrentUser();
|
||||
mCurrentUserIds = mUserManager.getProfiles(mCurrentUser).stream().map(
|
||||
userInfo -> userInfo.id).collect(Collectors.toList());
|
||||
|
||||
mPrivacyDialogBuilder = new PrivacyDialogBuilder(context, mPrivacyItems);
|
||||
}
|
||||
|
||||
private AppOpsController.Callback mCallback = new AppOpsController.Callback() {
|
||||
|
||||
@Override
|
||||
public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
|
||||
int userId = UserHandle.getUserId(uid);
|
||||
if (mCurrentUserIds.contains(userId)) {
|
||||
updatePrivacyList();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onFinishInflate() {
|
||||
mIconsContainer = findViewById(R.id.icons_container);
|
||||
mPrivacyChip = (LinearLayout) findViewById(R.id.car_privacy_chip);
|
||||
if (mPrivacyChip != null) {
|
||||
mPrivacyChip.setOnClickListener(this);
|
||||
setListening(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromWindow() {
|
||||
if (mPrivacyChip != null) {
|
||||
setListening(false);
|
||||
}
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
updatePrivacyList();
|
||||
Handler mUiHandler = new Handler(Looper.getMainLooper());
|
||||
mUiHandler.post(() -> {
|
||||
mActivityStarter.postStartActivityDismissingKeyguard(
|
||||
new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
|
||||
});
|
||||
}
|
||||
|
||||
private void setListening(boolean listen) {
|
||||
if (mListening == listen) {
|
||||
return;
|
||||
}
|
||||
mListening = listen;
|
||||
if (mListening) {
|
||||
sAppOpsController.addCallback(OPS, mCallback);
|
||||
updatePrivacyList();
|
||||
} else {
|
||||
sAppOpsController.removeCallback(OPS, mCallback);
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePrivacyList() {
|
||||
mPrivacyItems = mCurrentUserIds.stream()
|
||||
.flatMap(item -> sAppOpsController.getActiveAppOpsForUser(item).stream())
|
||||
.filter(Objects::nonNull)
|
||||
.map(item -> toPrivacyItem(item))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
mPrivacyDialogBuilder = new PrivacyDialogBuilder(mContext, mPrivacyItems);
|
||||
|
||||
Handler refresh = new Handler(Looper.getMainLooper());
|
||||
refresh.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateView();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private PrivacyItem toPrivacyItem(AppOpItem appOpItem) {
|
||||
PrivacyType type;
|
||||
switch (appOpItem.getCode()) {
|
||||
case AppOpsManager.OP_CAMERA:
|
||||
type = PrivacyType.TYPE_CAMERA;
|
||||
break;
|
||||
case AppOpsManager.OP_COARSE_LOCATION:
|
||||
type = PrivacyType.TYPE_LOCATION;
|
||||
break;
|
||||
case AppOpsManager.OP_FINE_LOCATION:
|
||||
type = PrivacyType.TYPE_LOCATION;
|
||||
break;
|
||||
case AppOpsManager.OP_RECORD_AUDIO:
|
||||
type = PrivacyType.TYPE_MICROPHONE;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
PrivacyApplication app = new PrivacyApplication(appOpItem.getPackageName(), mContext);
|
||||
return new PrivacyItem(type, app, appOpItem.getTimeStarted());
|
||||
}
|
||||
|
||||
// Should only be called if the mPrivacyDialogBuilder icons or app changed
|
||||
private void updateView() {
|
||||
if (mPrivacyItems.isEmpty()) {
|
||||
mPrivacyChip.setVisibility(GONE);
|
||||
return;
|
||||
}
|
||||
mPrivacyChip.setVisibility(VISIBLE);
|
||||
setIcons(mPrivacyDialogBuilder);
|
||||
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
private void setIcons(PrivacyDialogBuilder dialogBuilder) {
|
||||
mIconsContainer.removeAllViews();
|
||||
dialogBuilder.generateIcons().forEach(item -> {
|
||||
int size = mContext.getResources().getDimensionPixelSize(
|
||||
R.dimen.privacy_chip_icon_height);
|
||||
ImageView image = new ImageView(mContext);
|
||||
image.setImageDrawable(item);
|
||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(size, size);
|
||||
|
||||
int leftPadding = mContext.getResources().getDimensionPixelSize(
|
||||
R.dimen.privacy_chip_icon_padding_left);
|
||||
int topPadding = mContext.getResources().getDimensionPixelSize(
|
||||
R.dimen.privacy_chip_icon_padding_top);
|
||||
int rightPadding = mContext.getResources().getDimensionPixelSize(
|
||||
R.dimen.privacy_chip_icon_padding_right);
|
||||
int bottomPadding = mContext.getResources().getDimensionPixelSize(
|
||||
R.dimen.privacy_chip_icon_padding_bottom);
|
||||
image.setLayoutParams(layoutParams);
|
||||
image.setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
|
||||
mIconsContainer.addView(image);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.car.privacy;
|
||||
|
||||
import android.car.userlib.CarUserManagerHelper;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Class to hold the data for the applications that are using the AppOps permissions.
|
||||
*/
|
||||
public class PrivacyApplication {
|
||||
private static final String TAG = "PrivacyApplication";
|
||||
|
||||
private Drawable mIcon;
|
||||
private String mApplicationName;
|
||||
|
||||
public PrivacyApplication(String packageName, Context context) {
|
||||
try {
|
||||
CarUserManagerHelper carUserManagerHelper = new CarUserManagerHelper(context);
|
||||
ApplicationInfo app = context.getPackageManager()
|
||||
.getApplicationInfoAsUser(packageName, 0,
|
||||
carUserManagerHelper.getCurrentForegroundUserId());
|
||||
mIcon = context.getPackageManager().getApplicationIcon(app);
|
||||
mApplicationName = context.getPackageManager().getApplicationLabel(app).toString();
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
mApplicationName = packageName;
|
||||
Log.e(TAG, "Failed to to find package name", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the application name.
|
||||
*/
|
||||
public Drawable getIcon() {
|
||||
return mIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the application name.
|
||||
*/
|
||||
public String getApplicationName() {
|
||||
return mApplicationName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.car.privacy;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Helper class to build the {@link OngoingPrivacyDialog}
|
||||
*/
|
||||
public class PrivacyDialogBuilder {
|
||||
|
||||
private Map<PrivacyType, List<PrivacyItem>> mItemsByType;
|
||||
private PrivacyApplication mApplication;
|
||||
private Context mContext;
|
||||
|
||||
public PrivacyDialogBuilder(Context context, List<PrivacyItem> itemsList) {
|
||||
mContext = context;
|
||||
mItemsByType = itemsList.stream().filter(Objects::nonNull).collect(
|
||||
Collectors.groupingBy(PrivacyItem::getPrivacyType));
|
||||
List<PrivacyApplication> apps = itemsList.stream().filter(Objects::nonNull).map(
|
||||
PrivacyItem::getPrivacyApplication).distinct().collect(Collectors.toList());
|
||||
mApplication = apps.size() == 1 ? apps.get(0) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the icon id for all the {@link PrivacyItem} in the same order as of itemList.
|
||||
*/
|
||||
public List<Drawable> generateIcons() {
|
||||
return mItemsByType.keySet().stream().map(item -> item.getIconId(mContext)).collect(
|
||||
Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the application object.
|
||||
*/
|
||||
public PrivacyApplication getApplication() {
|
||||
return mApplication;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.car.privacy;
|
||||
|
||||
/**
|
||||
* Class for holding the data of each privacy item displayed in {@link OngoingPrivacyDialog}
|
||||
*/
|
||||
public class PrivacyItem {
|
||||
|
||||
private PrivacyType mPrivacyType;
|
||||
private PrivacyApplication mPrivacyApplication;
|
||||
|
||||
public PrivacyItem(PrivacyType privacyType, PrivacyApplication privacyApplication,
|
||||
long timeStarted) {
|
||||
this.mPrivacyType = privacyType;
|
||||
this.mPrivacyApplication = privacyApplication;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the application object.
|
||||
*/
|
||||
public PrivacyApplication getPrivacyApplication() {
|
||||
return mPrivacyApplication;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the privacy type for the application.
|
||||
*/
|
||||
public PrivacyType getPrivacyType() {
|
||||
return mPrivacyType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.car.privacy;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* Enum for storing data for camera, mic and location.
|
||||
*/
|
||||
public enum PrivacyType {
|
||||
TYPE_CAMERA(R.string.privacy_type_camera, com.android.internal.R.drawable.ic_camera),
|
||||
TYPE_LOCATION(R.string.privacy_type_location, R.drawable.stat_sys_location),
|
||||
TYPE_MICROPHONE(R.string.privacy_type_microphone, R.drawable.ic_mic_white);
|
||||
|
||||
private int mNameId;
|
||||
private int mIconId;
|
||||
|
||||
PrivacyType(int nameId, int iconId) {
|
||||
mNameId = nameId;
|
||||
mIconId = iconId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon Id.
|
||||
*/
|
||||
public Drawable getIconId(Context context) {
|
||||
return context.getResources().getDrawable(mIconId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name Id.
|
||||
*/
|
||||
public String getNameId(Context context) {
|
||||
return context.getResources().getString(mNameId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user