/*
 * Copyright (C) 2016 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.settings.accessibility;

import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;

import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.drawable.Drawable;
import android.os.storage.StorageManager;
import android.text.BidiFormatter;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;

import com.android.settings.R;

import java.util.Locale;

/**
 * Utility class for creating the dialog that asks users for explicit permission for an
 * accessibility service to access user data before the service is enabled
 */
public class AccessibilityServiceWarning {
    private static final View.OnTouchListener filterTouchListener = (View v, MotionEvent event) -> {
        // Filter obscured touches by consuming them.
        if (((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0)
                || ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED) != 0)) {
            if (event.getAction() == MotionEvent.ACTION_UP) {
                Toast.makeText(v.getContext(), R.string.touch_filtered_warning,
                        Toast.LENGTH_SHORT).show();
            }
            return true;
        }
        return false;
    };

    /** Returns a {@link Dialog} to be shown to confirm that they want to enable a service. */
    public static Dialog createCapabilitiesDialog(Context context,
            AccessibilityServiceInfo info, View.OnClickListener listener) {
        final AlertDialog ad = new AlertDialog.Builder(context)
                .setView(createEnableDialogContentView(context, info, listener))
                .create();

        Window window = ad.getWindow();
        WindowManager.LayoutParams params = window.getAttributes();
        params.privateFlags |= SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
        window.setAttributes(params);
        ad.create();
        ad.setCanceledOnTouchOutside(true);

        return ad;
    }

    /**
     * Returns whether the device is encrypted with legacy full disk encryption. Newer devices
     * should be using File Based Encryption.
     *
     * @return true if device is encrypted
     */
    private static boolean isFullDiskEncrypted() {
        return StorageManager.isNonDefaultBlockEncrypted();
    }

    private static View createEnableDialogContentView(Context context,
            AccessibilityServiceInfo info, View.OnClickListener listener) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(
                Context.LAYOUT_INFLATER_SERVICE);

        View content = inflater.inflate(R.layout.enable_accessibility_service_dialog_content,
                null);

        TextView encryptionWarningView = (TextView) content.findViewById(
                R.id.encryption_warning);
        if (isFullDiskEncrypted()) {
            String text = context.getString(R.string.enable_service_encryption_warning,
                    getServiceName(context, info));
            encryptionWarningView.setText(text);
            encryptionWarningView.setVisibility(View.VISIBLE);
        } else {
            encryptionWarningView.setVisibility(View.GONE);
        }

        final Drawable icon;
        if (info.getResolveInfo().getIconResource() == 0) {
            icon = ContextCompat.getDrawable(context, R.drawable.ic_accessibility_generic);
        } else {
            icon = info.getResolveInfo().loadIcon(context.getPackageManager());
        }

        ImageView permissionDialogIcon = content.findViewById(
                R.id.permissionDialog_icon);
        permissionDialogIcon.setImageDrawable(icon);

        TextView permissionDialogTitle = content.findViewById(R.id.permissionDialog_title);
        permissionDialogTitle.setText(context.getString(R.string.enable_service_title,
                getServiceName(context, info)));

        Button permissionAllowButton = content.findViewById(
                R.id.permission_enable_allow_button);
        Button permissionDenyButton = content.findViewById(
                R.id.permission_enable_deny_button);
        permissionAllowButton.setOnClickListener(listener);
        permissionAllowButton.setOnTouchListener(filterTouchListener);
        permissionDenyButton.setOnClickListener(listener);

        return content;
    }

    /** Returns a {@link Dialog} to be shown to confirm that they want to disable a service. */
    public static Dialog createDisableDialog(Context context,
            AccessibilityServiceInfo info, DialogInterface.OnClickListener listener) {
        final AlertDialog dialog = new AlertDialog.Builder(context)
                .setTitle(context.getString(R.string.disable_service_title,
                        info.getResolveInfo().loadLabel(context.getPackageManager())))
                .setMessage(context.getString(R.string.disable_service_message,
                        context.getString(R.string.accessibility_dialog_button_stop),
                        getServiceName(context, info)))
                .setCancelable(true)
                .setPositiveButton(R.string.accessibility_dialog_button_stop, listener)
                .setNegativeButton(R.string.accessibility_dialog_button_cancel, listener)
                .create();

        return dialog;
    }

    // Get the service name and bidi wrap it to protect from bidi side effects.
    private static CharSequence getServiceName(Context context, AccessibilityServiceInfo info) {
        final Locale locale = context.getResources().getConfiguration().getLocales().get(0);
        final CharSequence label =
                info.getResolveInfo().loadLabel(context.getPackageManager());
        return BidiFormatter.getInstance(locale).unicodeWrap(label);
    }
}
