package com.majeur.applicationsinfo;

import android.annotation.TargetApi;
import android.app.Fragment;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
import android.content.pm.PathPermission;
import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Bundle;
import android.os.PatternMatcher;
import android.os.RemoteException;
import android.text.format.Formatter;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.Button;
import android.widget.ExpandableListView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.majeur.applicationsinfo.utils.Tuple;
import com.majeur.applicationsinfo.utils.Utils;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DetailFragment extends Fragment {

    static final String FRAGMENT_TAG = "fragment_detail";
    static final String EXTRA_PACKAGE_NAME = "pkg";
    private static final String UID_STATS_PATH = "/proc/uid_stat/";
    private static final String UID_STATS_TR = "tcp_rcv";
    private static final String UID_STATS_RC = "tcp_snd";

    private static final int HEADER = 0;
    private static final int ACTIVITIES = 1;
    private static final int SERVICES = 2;
    private static final int RECEIVERS = 3;
    private static final int PROVIDERS = 4;
    private static final int USES_PERMISSIONS = 5;
    private static final int PERMISSIONS = 6;
    private static final int FEATURES = 7;
    private static final int CONFIGURATION = 8;
    private static final int SIGNATURES = 9;

    private PackageManager mPackageManager;
    private String mPackageName;
    private LayoutInflater mLayoutInflater;
    private PackageInfo mPackageInfo;
    private PackageStats mPackageStats;
    private DetailOverflowMenu mDetailOverflowMenu;

    private int mColorGrey1;
    private int mColorGrey2;
    private TypedArray mGroupTitleIds;

    private SimpleDateFormat mDateFormatter = new SimpleDateFormat("EE LLL dd yyyy kk:mm:ss");

    public static DetailFragment getInstance(String packageName) {
        DetailFragment detailFragment = new DetailFragment();
        Bundle args = new Bundle();
        args.putString(DetailFragment.EXTRA_PACKAGE_NAME, packageName);
        detailFragment.setArguments(args);

        return detailFragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPackageManager = getActivity().getPackageManager();
        mPackageName = getArguments().getString(EXTRA_PACKAGE_NAME);
        mColorGrey1 = getResources().getColor(R.color.grey_1);
        mColorGrey2 = getResources().getColor(R.color.grey_2);
        mDetailOverflowMenu = new DetailOverflowMenu(getActivity(), mPackageName);

        mGroupTitleIds = getResources().obtainTypedArray(R.array.group_titles);

        mPackageInfo = getPackageInfo(mPackageName);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mLayoutInflater = inflater;

        ExpandableListView listView = new ExpandableListView(getActivity());
        listView.setGroupIndicator(null);

        if (mPackageInfo == null) {
            Toast.makeText(getActivity(), R.string.app_not_installed, Toast.LENGTH_LONG).show();
        } else {
            listView.setAdapter(new Adapter());
        }

        return listView;
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mDetailOverflowMenu = null;
    }

    private PackageInfo getPackageInfo(String packageName) {
        try {
            return mPackageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS
                    | PackageManager.GET_ACTIVITIES | PackageManager.GET_RECEIVERS | PackageManager.GET_PROVIDERS
                    | PackageManager.GET_SERVICES | PackageManager.GET_URI_PERMISSION_PATTERNS
                    | PackageManager.GET_SIGNATURES | PackageManager.GET_CONFIGURATIONS);
        } catch (PackageManager.NameNotFoundException e) {
            return null;
        }
    }

    /**
     * Used to finish when user clicks on {@link android.app.ActionBar} arrow
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == android.R.id.home) {
            //onBackPressed();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * Create and populate header view
     */
    @TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
    private View getHeaderView(ViewGroup viewGroup) {
        View headerView = mLayoutInflater.inflate(R.layout.detail_header, viewGroup, false);

        ApplicationInfo applicationInfo = mPackageInfo.applicationInfo;

        TextView labelView = (TextView) headerView.findViewById(R.id.label);
        CharSequence label = applicationInfo.loadLabel(mPackageManager);
        if (getActivity() instanceof DetailActivity) {
            //Application is not in multi-pane mode, use ActionBar for label
            getActivity().setTitle(label);
            labelView.setVisibility(View.GONE);
        } else {
            //Application is in multi-pane mode, ActionBar is already used, use field in header
            //to display label
            labelView.setText(label);
        }

        TextView packageNameView = (TextView) headerView.findViewById(R.id.packageName);
        packageNameView.setText(mPackageName);

        ImageView iconView = (ImageView) headerView.findViewById(R.id.icon);
        iconView.setImageDrawable(applicationInfo.loadIcon(mPackageManager));

        if (Utils.isApi20()) {
            ImageView bannerView = (ImageView) headerView.findViewById(R.id.banner);
            bannerView.setImageDrawable(applicationInfo.loadBanner(mPackageManager));
        }

        TextView versionView = (TextView) headerView.findViewById(R.id.version);
        versionView.setText(mPackageInfo.versionName + " (" + mPackageInfo.versionCode + ")");

        TextView pathView = (TextView) headerView.findViewById(R.id.path);
        pathView.setText(applicationInfo.sourceDir);

        TextView isSystemAppView = (TextView) headerView.findViewById(R.id.isSystem);
        boolean isSystemApp = (mPackageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
        isSystemAppView.setText(isSystemApp ? R.string.system : R.string.user);

        TextView installDateView = (TextView) headerView.findViewById(R.id.installed_date);
        installDateView.setText(getString(R.string.installation) + ": " + getTime(mPackageInfo.firstInstallTime));

        TextView updateDateView = (TextView) headerView.findViewById(R.id.update_date);
        updateDateView.setText(getString(R.string.update) + ": " + getTime(mPackageInfo.lastUpdateTime));

        ImageButton overflowButton = (ImageButton) headerView.findViewById(R.id.detail_overflow);
        mDetailOverflowMenu.setView(overflowButton);

        TextView sharedUserId = (TextView) headerView.findViewById(R.id.sharedUserId);
        sharedUserId.setText(getString(R.string.shared_user_id) + ": " + mPackageInfo.sharedUserId);

        Tuple<String, String> uidNetStats = getNetStats(applicationInfo.uid);

        TextView netStatsTransmittedView = (TextView) headerView.findViewById(R.id.netstats_transmitted);
        netStatsTransmittedView.setText(getString(R.string.netstats_transmitted) + ": "
                + uidNetStats.getFirst());

        TextView netStatsReceivedView = (TextView) headerView.findViewById(R.id.netstats_received);
        netStatsReceivedView.setText(getString(R.string.netstats_received) + ": "
                + uidNetStats.getSecond());

        if (mPackageStats == null)
            getPackageSizeInfo(headerView);
        else
            onPackageStatsLoaded(headerView);

        return headerView;
    }

    private String getReadableSize(long size) {
        return Formatter.formatFileSize(getActivity(), size);
    }

    private Tuple<String, String> getNetStats(int uid) {
        Tuple<String, String> tuple = new Tuple<>(getReadableSize(0), getReadableSize(0));
        File uidStatsDir = new File(UID_STATS_PATH + uid);

        if (uidStatsDir.exists() && uidStatsDir.isDirectory()) {
            for (File child : uidStatsDir.listFiles()) {
                if (child.getName().equals(UID_STATS_TR))
                    tuple.setFirst(getReadableSize(Long.parseLong(Utils.getFileContent(child))));
                else if (child.getName().equals(UID_STATS_RC))
                    tuple.setSecond(getReadableSize(Long.parseLong(Utils.getFileContent(child))));
            }
        }
        return tuple;
    }

    public String getTime(long time) {
        Date date = new Date(time);
        return mDateFormatter.format(date);
    }

    /**
     * Load package sizes and update views if success
     *
     * @param view
     */
    private void getPackageSizeInfo(final View view) {
        try {
            Method getPackageSizeInfo = PackageManager.class.getMethod(
                    "getPackageSizeInfo", String.class, IPackageStatsObserver.class);

            getPackageSizeInfo.invoke(mPackageManager, mPackageName, new IPackageStatsObserver.Stub() {
                @Override
                public void onGetStatsCompleted(final PackageStats pStats, boolean succeeded)
                        throws RemoteException {
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mPackageStats = pStats;
                            onPackageStatsLoaded(view);
                        }
                    });
                }
            });
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * Update size views
     *
     * @param headerView
     */
    private void onPackageStatsLoaded(View headerView) {
        if (mPackageStats == null)
            return;

        TextView sizeCodeView = (TextView) headerView.findViewById(R.id.size_code);
        sizeCodeView.setText(getReadableSize(mPackageStats.codeSize));

        TextView sizeCacheView = (TextView) headerView.findViewById(R.id.size_cache);
        sizeCacheView.setText(getReadableSize(mPackageStats.cacheSize));

        TextView sizeDataView = (TextView) headerView.findViewById(R.id.size_data);
        sizeDataView.setText(getReadableSize(mPackageStats.dataSize));

        TextView sizeExtCodeView = (TextView) headerView.findViewById(R.id.size_ext_code);
        sizeExtCodeView.setText(getReadableSize(mPackageStats.externalCodeSize));

        TextView sizeExtCacheView = (TextView) headerView.findViewById(R.id.size_ext_cache);
        sizeExtCacheView.setText(getReadableSize(mPackageStats.externalCacheSize));

        TextView sizeExtDataView = (TextView) headerView.findViewById(R.id.size_ext_data);
        sizeExtDataView.setText(getReadableSize(mPackageStats.externalDataSize));

        TextView sizeObb = (TextView) headerView.findViewById(R.id.size_ext_obb);
        sizeObb.setText(getReadableSize(mPackageStats.externalObbSize));

        TextView sizeMedia = (TextView) headerView.findViewById(R.id.size_ext_media);
        sizeMedia.setText(getReadableSize(mPackageStats.externalMediaSize));
    }

    private class Adapter extends BaseExpandableListAdapter {

        /**
         * Returning total group titles count plus one for the header
         */
        @Override
        public int getGroupCount() {
            return mGroupTitleIds.length() + 1;
        }

        /**
         * {@link com.majeur.applicationsinfo.utils.Utils} method is used to prevent {@link java.lang.NullPointerException} when arrays are null.
         * In this case, we make sure that returned length is zero.
         */
        @Override
        public int getChildrenCount(int parentIndex) {
            return Utils.getArrayLengthSafely(getNeededArray(parentIndex));
        }

        /**
         * Return corresponding section's array
         */
        private Object[] getNeededArray(int index) {
            switch (index) {
                case HEADER:
                    return null;
                case ACTIVITIES:
                    return mPackageInfo.activities;
                case SERVICES:
                    return mPackageInfo.services;
                case RECEIVERS:
                    return mPackageInfo.receivers;
                case PROVIDERS:
                    return mPackageInfo.providers;
                case USES_PERMISSIONS:
                    return mPackageInfo.requestedPermissions;
                case PERMISSIONS:
                    return mPackageInfo.permissions;
                case FEATURES:
                    return mPackageInfo.reqFeatures;
                case CONFIGURATION:
                    return mPackageInfo.configPreferences;
                case SIGNATURES:
                    return mPackageInfo.signatures;
                default:
                    return null;
            }
        }

        /**
         * For HEADER value we return header view. In other case, we return simple {@link android.widget.TextView} with group title,
         * note that index in {@link android.content.res.TypedArray} is shifted to adapter implementation.
         */
        @Override
        public View getGroupView(int groupIndex, boolean b, View view, ViewGroup viewGroup) {
            if (groupIndex == HEADER)
                return getHeaderView(viewGroup);

            TextView textView;
            if (view instanceof TextView)
                textView = (TextView) view;
            else
                textView = (TextView) mLayoutInflater.inflate(R.layout.group_title_view, null);

            textView.setText(mGroupTitleIds.getString(groupIndex - 1) + " (" + getChildrenCount(groupIndex) + ")");
            return textView;
        }

        /**
         * Child click is not used
         */
        @Override
        public boolean isChildSelectable(int i, int i2) {
            return false;
        }

        /**
         * ViewHolder to use recycled views efficiently. Fields names are not expressive because we use
         * the same holder for any kind of view, and view are not all sames.
         */
        class ViewHolder {
            int currentViewType = -1;
            TextView textView1;
            TextView textView2;
            TextView textView3;
            TextView textView4;
            TextView textView5;
            TextView textView6;
            ImageView imageView;
            Button button;
        }

        /**
         * We return corresponding view, recycled view implementation is done in each method
         */
        @Override
        public View getChildView(int groupIndex, int childIndex, boolean b, View view, ViewGroup viewGroup) {
            switch (groupIndex) {
                case ACTIVITIES:
                    return getActivityView(viewGroup, view, childIndex);
                case SERVICES:
                    return getServicesView(viewGroup, view, childIndex);
                case RECEIVERS:
                    return getReceiverView(viewGroup, view, childIndex);
                case PROVIDERS:
                    return getProviderView(viewGroup, view, childIndex);
                case USES_PERMISSIONS:
                    return getUsesPermissionsView(view, childIndex);
                case PERMISSIONS:
                    return getPermissionsView(viewGroup, view, childIndex);
                case FEATURES:
                    return getFeaturesView(viewGroup, view, childIndex);
                case CONFIGURATION:
                    return getConfigurationView(viewGroup, view, childIndex);
                case SIGNATURES:
                    return getSignatureView(view, childIndex);
                default:
                    return null;
            }
        }

        /**
         * See below checkIfConvertViewMatch method.
         * Bored view inflation / creation.
         */
        private View getActivityView(ViewGroup viewGroup, View convertView, int index) {
            ViewHolder viewHolder;
            if (!checkIfConvertViewMatch(convertView, ACTIVITIES)) {
                convertView = mLayoutInflater.inflate(R.layout.detail_activities, viewGroup, false);

                viewHolder = new ViewHolder();
                viewHolder.currentViewType = ACTIVITIES;
                viewHolder.imageView = (ImageView) convertView.findViewById(R.id.icon);
                viewHolder.textView1 = (TextView) convertView.findViewById(R.id.label);
                viewHolder.textView2 = (TextView) convertView.findViewById(R.id.name);
                viewHolder.textView3 = (TextView) convertView.findViewById(R.id.taskAffinity);
                viewHolder.textView4 = (TextView) convertView.findViewById(R.id.launchMode);
                viewHolder.textView5 = (TextView) convertView.findViewById(R.id.orientation);
                viewHolder.textView6 = (TextView) convertView.findViewById(R.id.softInput);
                viewHolder.button = (Button) convertView.findViewById(R.id.launch);
                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            final ActivityInfo activityInfo = mPackageInfo.activities[index];
            convertView.setBackgroundColor(index % 2 == 0 ? mColorGrey1 : mColorGrey2);

            //Label
            viewHolder.textView1.setText(activityInfo.loadLabel(mPackageManager));

            //Name
            viewHolder.textView2.setText(activityInfo.name.replaceFirst(mPackageName, ""));

            //Icon
            viewHolder.imageView.setImageDrawable(activityInfo.loadIcon(mPackageManager));

            //TaskAffinity
            viewHolder.textView3.setText(getString(R.string.taskAffinity) + ": " + activityInfo.taskAffinity);

            //LaunchMode
            viewHolder.textView4.setText(getString(R.string.launch_mode) + ": " + Utils.getLaunchMode(activityInfo.launchMode));

            //Orientation
            viewHolder.textView5.setText(getString(R.string.orientation) + ": " + Utils.getOrientationString(activityInfo.screenOrientation));

            //SoftInput
            viewHolder.textView6.setText(getString(R.string.softInput) + ": " + Utils.getSoftInputString(activityInfo.softInputMode));


            Button launch = viewHolder.button;
            boolean isExported = activityInfo.exported;
            launch.setEnabled(isExported);
            if (isExported) {
                launch.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        Intent intent = new Intent();
                        intent.setClassName(mPackageName, activityInfo.name);
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        try {
                            startActivity(intent);
                        } catch (Exception e) {
                            Toast.makeText(getActivity(), e.toString(), Toast.LENGTH_LONG).show();
                        }
                    }
                });
            }
            return convertView;
        }

        /**
         * Boring view inflation / creation
         */
        private View getServicesView(ViewGroup viewGroup, View convertView, int index) {
            ViewHolder viewHolder;
            if (!checkIfConvertViewMatch(convertView, SERVICES)) {
                convertView = mLayoutInflater.inflate(R.layout.detail_activities, viewGroup, false);

                viewHolder = new ViewHolder();
                viewHolder.currentViewType = SERVICES;
                viewHolder.imageView = (ImageView) convertView.findViewById(R.id.icon);
                viewHolder.textView1 = (TextView) convertView.findViewById(R.id.label);
                viewHolder.textView2 = (TextView) convertView.findViewById(R.id.name);
                viewHolder.textView3 = (TextView) convertView.findViewById(R.id.launchMode);
                convertView.findViewById(R.id.taskAffinity).setVisibility(View.GONE);
                convertView.findViewById(R.id.orientation).setVisibility(View.GONE);
                convertView.findViewById(R.id.softInput).setVisibility(View.GONE);
                convertView.findViewById(R.id.launch).setVisibility(View.GONE);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            final ServiceInfo serviceInfo = mPackageInfo.services[index];
            convertView.setBackgroundColor(index % 2 == 0 ? mColorGrey1 : mColorGrey2);

            //Label
            viewHolder.textView1.setText(serviceInfo.loadLabel(mPackageManager));

            //Name
            viewHolder.textView2.setText(serviceInfo.name.replaceFirst(mPackageName, ""));

            //Icon
            viewHolder.imageView.setImageDrawable(serviceInfo.loadIcon(mPackageManager));

            //Flags
            viewHolder.textView3.setText(getString(R.string.flags) + ": " + Utils.getServiceFlagsString(serviceInfo.flags));

            return convertView;
        }

        /**
         * Boring view inflation / creation
         */
        private View getReceiverView(ViewGroup viewGroup, View convertView, int index) {
            ViewHolder viewHolder;
            if (!checkIfConvertViewMatch(convertView, RECEIVERS)) {
                convertView = mLayoutInflater.inflate(R.layout.detail_activities, viewGroup, false);

                viewHolder = new ViewHolder();
                viewHolder.currentViewType = RECEIVERS;
                viewHolder.imageView = (ImageView) convertView.findViewById(R.id.icon);
                viewHolder.textView1 = (TextView) convertView.findViewById(R.id.label);
                viewHolder.textView2 = (TextView) convertView.findViewById(R.id.name);
                viewHolder.textView3 = (TextView) convertView.findViewById(R.id.taskAffinity);
                viewHolder.textView4 = (TextView) convertView.findViewById(R.id.launchMode);
                viewHolder.textView5 = (TextView) convertView.findViewById(R.id.orientation);
                viewHolder.textView6 = (TextView) convertView.findViewById(R.id.softInput);
                convertView.findViewById(R.id.launch).setVisibility(View.GONE);
                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            final ActivityInfo activityInfo = mPackageInfo.receivers[index];
            convertView.setBackgroundColor(index % 2 == 0 ? mColorGrey1 : mColorGrey2);

            //Label
            viewHolder.textView1.setText(activityInfo.loadLabel(mPackageManager));

            //Name
            viewHolder.textView2.setText(activityInfo.name.replaceFirst(mPackageName, ""));

            //Icon
            viewHolder.imageView.setImageDrawable(activityInfo.loadIcon(mPackageManager));

            //TaskAffinity
            viewHolder.textView3.setText(getString(R.string.taskAffinity) + ": " + activityInfo.taskAffinity);

            //LaunchMode
            viewHolder.textView4.setText(getString(R.string.launch_mode) + ": " + Utils.getLaunchMode(activityInfo.launchMode));

            //Orientation
            viewHolder.textView5.setText(getString(R.string.orientation) + ": " + Utils.getOrientationString(activityInfo.screenOrientation));

            //SoftInput
            viewHolder.textView6.setText(getString(R.string.softInput) + ": " + Utils.getSoftInputString(activityInfo.softInputMode));

            return convertView;
        }

        /**
         * Boring view inflation / creation
         */
        private View getProviderView(ViewGroup viewGroup, View convertView, int index) {
            ViewHolder viewHolder;
            if (!checkIfConvertViewMatch(convertView, PROVIDERS)) {
                convertView = mLayoutInflater.inflate(R.layout.detail_activities, viewGroup, false);

                viewHolder = new ViewHolder();
                viewHolder.currentViewType = PROVIDERS;
                viewHolder.imageView = (ImageView) convertView.findViewById(R.id.icon);
                viewHolder.textView1 = (TextView) convertView.findViewById(R.id.label);
                viewHolder.textView2 = (TextView) convertView.findViewById(R.id.name);
                viewHolder.textView3 = (TextView) convertView.findViewById(R.id.launchMode);
                viewHolder.textView4 = (TextView) convertView.findViewById(R.id.orientation);
                viewHolder.textView5 = (TextView) convertView.findViewById(R.id.softInput);
                viewHolder.textView6 = (TextView) convertView.findViewById(R.id.taskAffinity);
                convertView.findViewById(R.id.launch).setVisibility(View.GONE);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            final ProviderInfo providerInfo = mPackageInfo.providers[index];
            convertView.setBackgroundColor(index % 2 == 0 ? mColorGrey1 : mColorGrey2);

            //Label
            viewHolder.textView1.setText(providerInfo.loadLabel(mPackageManager));

            //Name
            viewHolder.textView2.setText(providerInfo.name.replaceFirst(mPackageName, ""));

            //Icon
            viewHolder.imageView.setImageDrawable(providerInfo.loadIcon(mPackageManager));

            //Uri permission
            viewHolder.textView3.setText(getString(R.string.grant_uri_permission) + ": " + providerInfo.grantUriPermissions);

            //Path permissions
            PathPermission[] pathPermissions = providerInfo.pathPermissions;
            String finalString;
            if (pathPermissions != null) {
                StringBuilder builder = new StringBuilder();
                String read = getString(R.string.read);
                String write = getString(R.string.write);
                for (PathPermission permission : pathPermissions) {
                    builder.append(read + ": " + permission.getReadPermission());
                    builder.append("/");
                    builder.append(write + ": " + permission.getWritePermission());
                    builder.append(", ");
                }
                Utils.checkStringBuilderEnd(builder);
                finalString = builder.toString();
            } else
                finalString = "null";
            viewHolder.textView4.setText(getString(R.string.path_permissions) + ": " + finalString);

            //Pattern matchers
            PatternMatcher[] patternMatchers = providerInfo.uriPermissionPatterns;
            String finalString1;
            if (patternMatchers != null) {
                StringBuilder builder = new StringBuilder();
                for (PatternMatcher patternMatcher : patternMatchers) {
                    builder.append(patternMatcher.toString());
                    builder.append(", ");
                }
                Utils.checkStringBuilderEnd(builder);
                finalString1 = builder.toString();
            } else
                finalString1 = "null";
            viewHolder.textView5.setText(getString(R.string.patterns_allowed) + ": " + finalString1);

            //Authority
            viewHolder.textView6.setText(getString(R.string.authority) + ": " + providerInfo.authority);

            return convertView;
        }

        /**
         * We do not need complex views, Use recycled view if possible
         */
        private View getUsesPermissionsView(View convertView, int index) {
            if (!(convertView instanceof TextView)) {
                convertView = new TextView(getActivity());
            }

            TextView textView = (TextView) convertView;
            textView.setTextIsSelectable(true);
            textView.setText(mPackageInfo.requestedPermissions[index]);
            textView.setBackgroundColor(index % 2 == 0 ? mColorGrey1 : mColorGrey2);
            int size = getActivity().getResources().getDimensionPixelSize(R.dimen.header_text_margin);
            textView.setPadding(size, 0, size, 0);

            return convertView;
        }

        /**
         * Boring view inflation / creation
         */
        private View getPermissionsView(ViewGroup viewGroup, View convertView, int index) {
            ViewHolder viewHolder;
            if (!checkIfConvertViewMatch(convertView, PERMISSIONS)) {
                convertView = mLayoutInflater.inflate(R.layout.detail_activities, viewGroup, false);

                viewHolder = new ViewHolder();
                viewHolder.currentViewType = PERMISSIONS;
                viewHolder.imageView = (ImageView) convertView.findViewById(R.id.icon);
                viewHolder.textView1 = (TextView) convertView.findViewById(R.id.label);
                viewHolder.textView2 = (TextView) convertView.findViewById(R.id.name);
                viewHolder.textView3 = (TextView) convertView.findViewById(R.id.taskAffinity);
                viewHolder.textView4 = (TextView) convertView.findViewById(R.id.launchMode);
                viewHolder.textView5 = (TextView) convertView.findViewById(R.id.orientation);
                convertView.findViewById(R.id.softInput).setVisibility(View.GONE);
                convertView.findViewById(R.id.launch).setVisibility(View.GONE);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            final PermissionInfo permissionInfo = mPackageInfo.permissions[index];
            convertView.setBackgroundColor(index % 2 == 0 ? mColorGrey1 : mColorGrey2);

            //Label
            viewHolder.textView1.setText(permissionInfo.loadLabel(mPackageManager));

            //Name
            viewHolder.textView2.setText(permissionInfo.name.replaceFirst(mPackageName, ""));

            //Icon
            viewHolder.imageView.setImageDrawable(permissionInfo.loadIcon(mPackageManager));

            //Description
            viewHolder.textView3.setText(permissionInfo.loadDescription(mPackageManager));

            //LaunchMode
            viewHolder.textView4.setText(getString(R.string.group) + ": " + permissionInfo.group);

            //Protection level
            viewHolder.textView5.setText(getString(R.string.protection_level) + ": " + Utils.getProtectionLevelString(permissionInfo.protectionLevel));

            return convertView;
        }

        /**
         * Boring view inflation / creation
         */
        private View getFeaturesView(ViewGroup viewGroup, View convertView, int index) {
            ViewHolder viewHolder;
            if (!checkIfConvertViewMatch(convertView, FEATURES)) {
                convertView = mLayoutInflater.inflate(R.layout.detail_features, viewGroup, false);

                viewHolder = new ViewHolder();
                viewHolder.currentViewType = FEATURES;
                viewHolder.textView1 = (TextView) convertView.findViewById(R.id.name);
                viewHolder.textView2 = (TextView) convertView.findViewById(R.id.flags);
                viewHolder.textView3 = (TextView) convertView.findViewById(R.id.gles_ver);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            final FeatureInfo featureInfo = mPackageInfo.reqFeatures[index];
            convertView.setBackgroundColor(index % 2 == 0 ? mColorGrey1 : mColorGrey2);

            //Name
            viewHolder.textView1.setText(featureInfo.name);

            //Falgs
            viewHolder.textView2.setText(getString(R.string.flags) + ": " + Utils.getFeatureFlagsString(featureInfo.flags));

            //GLES ver
            viewHolder.textView3.setText(getString(R.string.gles_ver) + ": " + featureInfo.reqGlEsVersion);

            return convertView;
        }

        /**
         * Boring view inflation / creation
         */
        private View getConfigurationView(ViewGroup viewGroup, View convertView, int index) {
            ViewHolder viewHolder;
            if (!checkIfConvertViewMatch(convertView, CONFIGURATION)) {
                convertView = mLayoutInflater.inflate(R.layout.detail_features, viewGroup);

                viewHolder = new ViewHolder();
                viewHolder.currentViewType = CONFIGURATION;
                viewHolder.textView1 = (TextView) convertView.findViewById(R.id.name);
                viewHolder.textView2 = (TextView) convertView.findViewById(R.id.flags);
                convertView.findViewById(R.id.gles_ver).setVisibility(View.GONE);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            final ConfigurationInfo configurationInfo = mPackageInfo.configPreferences[index];
            convertView.setBackgroundColor(index % 2 == 0 ? mColorGrey1 : mColorGrey2);

            //GLES ver
            viewHolder.textView1.setText(getString(R.string.gles_ver) + ": " + configurationInfo.reqGlEsVersion);

            //Falg
            viewHolder.textView2.setText(getString(R.string.input_features) + ": " +
                    Utils.getInputFeaturesString(configurationInfo.reqInputFeatures));

            return convertView;
        }

        /**
         * We do not need complex views, Use recycled view if possible
         */
        private View getSignatureView(View convertView, int index) {
            if (!(convertView instanceof TextView)) {
                convertView = new TextView(getActivity());
            }

            TextView textView = (TextView) convertView;
            textView.setText(mPackageInfo.signatures[index].toCharsString());
            textView.setBackgroundColor(index % 2 == 0 ? mColorGrey1 : mColorGrey2);
            textView.setTextIsSelectable(true);
            int size = getActivity().getResources().getDimensionPixelSize(R.dimen.header_text_margin);
            textView.setPadding(size, 0, size, 0);

            return convertView;
        }

        /**
         * Here we check if recycled view match requested type. Tag can be null if recycled view comes from
         * groups that doesn't implement {@link ViewHolder}, such as groups that use only a simple text view.
         */
        private boolean checkIfConvertViewMatch(View convertView, int requestedGroup) {
            return convertView != null && convertView.getTag() != null && ((ViewHolder) convertView.getTag()).currentViewType == requestedGroup;
        }

        /**
         * Unused methods
         */

        @Override
        public Object getGroup(int i) {
            return null;
        }

        @Override
        public Object getChild(int i, int i2) {
            return null;
        }

        @Override
        public long getGroupId(int i) {
            return 0;
        }

        @Override
        public long getChildId(int i, int i2) {
            return 0;
        }

        @Override
        public boolean hasStableIds() {
            return false;
        }
    }
}