blob: c0bafb7a0d16cb518ed79255f027d9a46d283265 [file] [log] [blame]
/*
* Copyright (C) 2020 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.notification.row;
import static android.service.notification.NotificationAssistantService.FEEDBACK_RATING;
import static com.android.systemui.statusbar.notification.AssistantFeedbackController.STATUS_ALERTED;
import static com.android.systemui.statusbar.notification.AssistantFeedbackController.STATUS_DEMOTED;
import static com.android.systemui.statusbar.notification.AssistantFeedbackController.STATUS_PROMOTED;
import static com.android.systemui.statusbar.notification.AssistantFeedbackController.STATUS_SILENCED;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.RemoteException;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.text.Html;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsContent {
private static final String TAG = "FeedbackInfo";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private NotificationGuts mGutsContainer;
private NotificationListenerService.Ranking mRanking;
private PackageManager mPm;
private String mAppName;
private String mPkg;
private NotificationEntry mEntry;
private NotificationEntryManager mNotificationEntryManager;
private IStatusBarService mStatusBarService;
private AssistantFeedbackController mFeedbackController;
private NotificationGutsManager mNotificationGutsManager;
private NotificationMenuRowPlugin mMenuRowPlugin;
private ExpandableNotificationRow mExpandableNotificationRow;
public FeedbackInfo(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void bindGuts(
final PackageManager pm,
final StatusBarNotification sbn,
final NotificationEntry entry,
final ExpandableNotificationRow row,
final AssistantFeedbackController controller) {
mPkg = sbn.getPackageName();
mPm = pm;
mEntry = entry;
mExpandableNotificationRow = row;
mRanking = entry.getRanking();
mFeedbackController = controller;
mAppName = mPkg;
mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
mStatusBarService = Dependency.get(IStatusBarService.class);
mNotificationGutsManager = Dependency.get(NotificationGutsManager.class);
bindHeader();
bindPrompt();
}
private void bindHeader() {
// Package name
Drawable pkgicon = null;
ApplicationInfo info;
try {
info = mPm.getApplicationInfo(mPkg,
PackageManager.MATCH_UNINSTALLED_PACKAGES
| PackageManager.MATCH_DISABLED_COMPONENTS
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DIRECT_BOOT_AWARE);
if (info != null) {
mAppName = String.valueOf(mPm.getApplicationLabel(info));
pkgicon = mPm.getApplicationIcon(info);
}
} catch (PackageManager.NameNotFoundException e) {
// app is gone, just show package name and generic icon
pkgicon = mPm.getDefaultActivityIcon();
}
((ImageView) findViewById(R.id.pkg_icon)).setImageDrawable(pkgicon);
((TextView) findViewById(R.id.pkg_name)).setText(mAppName);
}
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
if (mGutsContainer != null
&& event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
if (mGutsContainer.isExposed()) {
event.getText().add(mContext.getString(
R.string.notification_channel_controls_opened_accessibility, mAppName));
} else {
event.getText().add(mContext.getString(
R.string.notification_channel_controls_closed_accessibility, mAppName));
}
}
}
private void bindPrompt() {
final TextView prompt = findViewById(R.id.prompt);
final TextView yes = findViewById(R.id.yes);
final TextView no = findViewById(R.id.no);
yes.setVisibility(View.VISIBLE);
no.setVisibility(View.VISIBLE);
yes.setOnClickListener(this::positiveFeedback);
no.setOnClickListener(this::negativeFeedback);
prompt.setText(Html.fromHtml(getPrompt()));
}
@SuppressLint("DefaultLocale")
private String getPrompt() {
StringBuilder sb = new StringBuilder();
int status = mFeedbackController.getFeedbackStatus(mEntry);
if (DEBUG) {
sb.append(String.format(
"[DEBUG]: oldImportance=%d, newImportance=%d, ranking=%f\n\n",
mRanking.getChannel().getImportance(), mRanking.getImportance(),
mRanking.getRankingScore()));
}
if (status == STATUS_ALERTED) {
sb.append(mContext.getText(R.string.feedback_alerted));
} else if (status == STATUS_SILENCED) {
sb.append(mContext.getText(R.string.feedback_silenced));
} else if (status == STATUS_PROMOTED) {
sb.append(mContext.getText(R.string.feedback_promoted));
} else if (status == STATUS_DEMOTED) {
sb.append(mContext.getText(R.string.feedback_demoted));
}
sb.append(" ");
sb.append(mContext.getText(R.string.feedback_prompt));
return sb.toString();
}
private void positiveFeedback(View v) {
mGutsContainer.closeControls(v, false);
handleFeedback(true);
}
private void negativeFeedback(View v) {
mMenuRowPlugin = mExpandableNotificationRow.getProvider();
NotificationMenuRowPlugin.MenuItem menuItem = null;
if (mMenuRowPlugin != null) {
menuItem = mMenuRowPlugin.getLongpressMenuItem(mContext);
}
mGutsContainer.closeControls(v, false);
mNotificationGutsManager.openGuts(mExpandableNotificationRow, 0, 0, menuItem);
handleFeedback(false);
}
private void handleFeedback(boolean positive) {
Bundle feedback = new Bundle();
feedback.putInt(FEEDBACK_RATING, positive ? 1 : -1);
sendFeedbackToAssistant(feedback);
}
private void sendFeedbackToAssistant(Bundle feedback) {
if (!mFeedbackController.isFeedbackEnabled()) {
return;
}
try {
mStatusBarService.onNotificationFeedbackReceived(mRanking.getKey(), feedback);
} catch (RemoteException e) {
if (DEBUG) {
Log.e(TAG, "Failed to send feedback to assistant", e);
}
}
}
private void closeControls(View v) {
mGutsContainer.closeControls(v, false);
}
@Override
public void setGutsParent(NotificationGuts guts) {
mGutsContainer = guts;
}
@Override
public View getContentView() {
return this;
}
@Override
public int getActualHeight() {
return getHeight();
}
@Override
public boolean handleCloseControls(boolean save, boolean force) {
return false;
}
@Override
public boolean willBeRemoved() {
return false;
}
@Override
public boolean shouldBeSaved() {
return false;
}
@Override
public boolean needsFalsingProtection() {
return false;
}
}