/*
 * 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 android.car.usb.handler;

import static android.content.Intent.ACTION_USER_UNLOCKED;

import android.annotation.Nullable;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;

import java.util.List;

/**
 * Activity to handle USB device attached.
 * <p>
 * When user plugs in USB device: a) Device was used before and user selected handler for it. In
 * this case handler will be launched. b) Device has not handler assigned. In this case supported
 * handlers will be captured, and user will be presented with choice to assign default handler.
 * After that handler will be launched.
 *
 * <p>Note: Activity launched by the system and by {@link BootUsbService}.
 */
public class UsbHostManagementActivity extends Activity {
    private static final String TAG = UsbHostManagementActivity.class.getSimpleName();

    private HandlersAdapter mListAdapter;
    private ListView mHandlersList;
    private TextView mHandlerTitle;
    private LinearLayout mUsbHandlersDialog;
    private UsbHostController mController;
    private PackageManager mPackageManager;

    private final ResolveBroadcastReceiver mResolveBroadcastReceiver
            = new ResolveBroadcastReceiver();
    private boolean mReceiverRegistered = false;

    private void unregisterResolveBroadcastReceiver() {
        if (mReceiverRegistered) {
            Log.d(TAG, "Unregistering USER_UNLOCKED broadcast");
            unregisterReceiver(mResolveBroadcastReceiver);
            mReceiverRegistered = false;
        }
    }

    private void processDevice() {
        UsbDevice connectedDevice = getDevice();

        if (connectedDevice != null) {
            Log.d(TAG, "Processing device: " + connectedDevice.getProductName());
            mController.processDevice(connectedDevice);
        } else {
            Log.d(TAG, "Device not found.");
            finish();
        }
    }

    private class ResolveBroadcastReceiver extends BroadcastReceiver {
        public void onReceive(Context context, Intent intent) {
            // We could have been unregistered after receiving the intent but before processing it,
            // so make sure we are still registered.
            if (mReceiverRegistered) {
                unregisterResolveBroadcastReceiver();
                processDevice();
            }
        }
    }

    private final AdapterView.OnItemClickListener mHandlerClickListener =
            new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
            UsbDeviceSettings settings = (UsbDeviceSettings) parent.getItemAtPosition(position);
            settings.setDefaultHandler(true);
            mController.applyDeviceSettings(settings);
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.usb_host);
        mUsbHandlersDialog = findViewById(R.id.usb_handlers_dialog);
        mHandlersList = findViewById(R.id.usb_handlers_list);
        mHandlerTitle = findViewById(R.id.usb_handler_heading);
        mListAdapter = new HandlersAdapter(this);
        mHandlersList.setAdapter(mListAdapter);
        mHandlersList.setOnItemClickListener(mHandlerClickListener);
        mController = new UsbHostController(this, new UsbCallbacks());
        mPackageManager = getPackageManager();
        hideDialog();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mController.release();
    }

    @Override
    public void onPause() {
        super.onPause();
        unregisterResolveBroadcastReceiver();
    }

    @Override
    public void onResume() {
        super.onResume();

        UserManager userManager = getSystemService(UserManager.class);
        // Checks should pass if activity is started from BootUsbService, but necessary for system
        // calls.
        if (userManager.isUserUnlocked() || getUserId() == UserHandle.USER_SYSTEM) {
            processDevice();
        } else {
            Log.d(TAG, "Waiting for user unlocked to process device.");
            mReceiverRegistered = true;
            registerReceiver(mResolveBroadcastReceiver, new IntentFilter(ACTION_USER_UNLOCKED),
                    Context.RECEIVER_NOT_EXPORTED);
            // in case the car was unlocked while the receiver was being registered
            if (userManager.isUserUnlocked()) {
                mResolveBroadcastReceiver.onReceive(this, new Intent(ACTION_USER_UNLOCKED));
            }
        }
    }

    private void hideDialog() {
        setTheme(android.R.style.Theme_Translucent);
        if (mUsbHandlersDialog != null) {
            mUsbHandlersDialog.setVisibility(View.GONE);
        }
    }

    private void showDialog() {
        setTranslucent(false);
        setTheme(android.R.style.Theme_DeviceDefault_Dialog);
        if (mUsbHandlersDialog != null) {
            mUsbHandlersDialog.setVisibility(View.VISIBLE);
        }
    }

    class UsbCallbacks implements UsbHostController.UsbHostControllerCallbacks {
        private boolean mProcessing = false;

        @Override
        public void shutdown() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    finish();
                }
            });
        }

        @Override
        public void processingStarted() {
            mProcessing = true;
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (mProcessing && !mListAdapter.isEmpty()) {
                        showDialog();
                    }
                }
            });
        }

        @Override
        public void titleChanged(final String title) {
            runOnUiThread(() -> mHandlerTitle.setText(title));
        }

        @Override
        public void optionsUpdated(final List<UsbDeviceSettings> options) {
            if (options != null && !options.isEmpty()) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (mProcessing) {
                            showDialog();
                        }
                        mListAdapter.clear();
                        mListAdapter.addAll(options);
                    }
                });
            }
        }
    }

    @Override
    public void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
    }

    @Nullable
    private UsbDevice getDevice() {
        if (!UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(getIntent().getAction())) {
            return null;
        }
        return (UsbDevice) getIntent().getParcelableExtra(UsbManager.EXTRA_DEVICE);
    }

    private class HandlersAdapter extends ArrayAdapter<UsbDeviceSettings> {
        class HandlerHolder {
            public TextView mAppName;
            public ImageView mAppIcon;
        }

        HandlersAdapter(Context context) {
            super(context, R.layout.usb_handler_row);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View rowView = convertView;
            if (rowView == null) {
                rowView = getLayoutInflater().inflate(R.layout.usb_handler_row, null);
                HandlerHolder holder = new HandlerHolder();
                holder.mAppName = (TextView) rowView.findViewById(R.id.usb_handler_title);
                holder.mAppIcon = (ImageView) rowView.findViewById(R.id.usb_handler_icon);
                rowView.setTag(holder);
            }

            HandlerHolder holder = (HandlerHolder) rowView.getTag();
            ComponentName handler = getItem(position).getHandler();

            try {
                ApplicationInfo appInfo =
                        mPackageManager.getApplicationInfo(handler.getPackageName(), 0);
                holder.mAppName.setText(appInfo.loadLabel(mPackageManager));
                holder.mAppIcon.setImageDrawable(appInfo.loadIcon(mPackageManager));
            } catch (NameNotFoundException e) {
                Log.e(TAG, "Handling package not found: " + handler.getPackageName());
                holder.mAppName.setText(handler.flattenToShortString());
                holder.mAppIcon.setImageResource(android.R.color.transparent);
            }
            return rowView;
        }
    }
}
