/*
 * 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.statusbar.notification;

import static com.android.systemui.media.MediaDataManagerKt.isMediaNotification;

import android.Manifest;
import android.app.AppGlobals;
import android.app.Notification;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.service.notification.StatusBarNotification;

import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.ShadeController;

import javax.inject.Inject;

/** Component which manages the various reasons a notification might be filtered out.*/
// TODO: delete NotificationFilter.java after migrating to new NotifPipeline b/145659174.
//  Notification filtering is taken care of across the different Coordinators (mostly
//  KeyguardCoordinator.java)
@SysUISingleton
public class NotificationFilter {

    private final StatusBarStateController mStatusBarStateController;
    private final Boolean mIsMediaFlagEnabled;

    private NotificationEntryManager.KeyguardEnvironment mEnvironment;
    private ShadeController mShadeController;
    private ForegroundServiceController mFsc;
    private NotificationLockscreenUserManager mUserManager;

    @Inject
    public NotificationFilter(
            StatusBarStateController statusBarStateController,
            MediaFeatureFlag mediaFeatureFlag) {
        mStatusBarStateController = statusBarStateController;
        mIsMediaFlagEnabled = mediaFeatureFlag.getEnabled();
    }

    private NotificationEntryManager.KeyguardEnvironment getEnvironment() {
        if (mEnvironment == null) {
            mEnvironment = Dependency.get(NotificationEntryManager.KeyguardEnvironment.class);
        }
        return mEnvironment;
    }

    private ShadeController getShadeController() {
        if (mShadeController == null) {
            mShadeController = Dependency.get(ShadeController.class);
        }
        return mShadeController;
    }

    private ForegroundServiceController getFsc() {
        if (mFsc == null) {
            mFsc = Dependency.get(ForegroundServiceController.class);
        }
        return mFsc;
    }

    private NotificationLockscreenUserManager getUserManager() {
        if (mUserManager == null) {
            mUserManager = Dependency.get(NotificationLockscreenUserManager.class);
        }
        return mUserManager;
    }


    /**
     * @return true if the provided notification should NOT be shown right now.
     */
    public boolean shouldFilterOut(NotificationEntry entry) {
        final StatusBarNotification sbn = entry.getSbn();
        if (!(getEnvironment().isDeviceProvisioned()
                || showNotificationEvenIfUnprovisioned(sbn))) {
            return true;
        }

        if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
            return true;
        }

        if (getUserManager().isLockscreenPublicMode(sbn.getUserId())
                && (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
                        || getUserManager().shouldHideNotifications(sbn.getUserId())
                        || getUserManager().shouldHideNotifications(sbn.getKey()))) {
            return true;
        }

        if (mStatusBarStateController.isDozing() && entry.shouldSuppressAmbient()) {
            return true;
        }

        if (!mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList()) {
            return true;
        }

        if (entry.getRanking().isSuspended()) {
            return true;
        }

        if (getFsc().isDisclosureNotification(sbn)
                && !getFsc().isDisclosureNeededForUser(sbn.getUserId())) {
            // this is a foreground-service disclosure for a user that does not need to show one
            return true;
        }

        if (mIsMediaFlagEnabled && isMediaNotification(sbn)) {
            return true;
        }
        return false;
    }

    // Q: What kinds of notifications should show during setup?
    // A: Almost none! Only things coming from packages with permission
    // android.permission.NOTIFICATION_DURING_SETUP that also have special "kind" tags marking them
    // as relevant for setup (see below).
    private static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
        return showNotificationEvenIfUnprovisioned(AppGlobals.getPackageManager(), sbn);
    }

    @VisibleForTesting
    static boolean showNotificationEvenIfUnprovisioned(IPackageManager packageManager,
            StatusBarNotification sbn) {
        return checkUidPermission(packageManager, Manifest.permission.NOTIFICATION_DURING_SETUP,
                sbn.getUid()) == PackageManager.PERMISSION_GRANTED
                && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
    }

    private static int checkUidPermission(IPackageManager packageManager, String permission,
            int uid) {
        try {
            return packageManager.checkUidPermission(permission, uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}
