content: package com.zzzmode.appopsx.ui.core;
  
  import android.Manifest;
  import android.app.AppOpsManager;
  import android.content.Context;
  import android.content.Intent;
  import android.content.SharedPreferences;
  import android.content.pm.ApplicationInfo;
  import android.content.pm.PackageInfo;
  import android.content.pm.PackageManager;
  import android.content.pm.PermissionInfo;
  import android.content.pm.ShortcutInfo;
  import android.content.pm.ShortcutManager;
  import android.content.pm.UserInfo;
  import android.graphics.Bitmap;
  import android.graphics.Canvas;
  import android.graphics.drawable.BitmapDrawable;
  import android.graphics.drawable.Drawable;
  import android.graphics.drawable.Icon;
  import android.os.Build;
  import android.preference.PreferenceManager;
  import android.text.TextUtils;
  import android.util.SparseArray;
  import android.util.SparseIntArray;
  import androidx.annotation.RequiresApi;
  import androidx.core.text.BidiFormatter;
  import androidx.core.util.Pair;
  import com.zzzmode.appopsx.BuildConfig;
  import com.zzzmode.appopsx.R;
  import com.zzzmode.appopsx.common.FixCompat;
  import com.zzzmode.appopsx.common.OpEntry;
  import com.zzzmode.appopsx.common.OpsResult;
  import com.zzzmode.appopsx.common.OtherOp;
  import com.zzzmode.appopsx.common.PackageOps;
  import com.zzzmode.appopsx.ui.model.AppInfo;
  import com.zzzmode.appopsx.ui.model.AppPermissions;
  import com.zzzmode.appopsx.ui.model.OpEntryInfo;
  import com.zzzmode.appopsx.ui.model.PermissionChildItem;
  import com.zzzmode.appopsx.ui.model.PermissionGroup;
  import com.zzzmode.appopsx.ui.model.PreAppInfo;
  import com.zzzmode.appopsx.ui.permission.AppPermissionActivity;
  import io.reactivex.Observable;
  import io.reactivex.ObservableEmitter;
  import io.reactivex.ObservableOnSubscribe;
  import io.reactivex.ObservableSource;
  import io.reactivex.Single;
  import io.reactivex.SingleEmitter;
  import io.reactivex.SingleOnSubscribe;
  import io.reactivex.android.schedulers.AndroidSchedulers;
  import io.reactivex.annotations.NonNull;
  import io.reactivex.functions.BiConsumer;
  import io.reactivex.functions.Function;
  import io.reactivex.functions.Predicate;
  import io.reactivex.internal.operators.single.SingleJust;
  import io.reactivex.observers.ResourceSingleObserver;
  import io.reactivex.schedulers.Schedulers;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.HashMap;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.Set;
  import java.util.concurrent.Callable;
  
  /**
   * Created by zl on 2017/1/17.
   */
  
  public class Helper {
  
    public static final class permission_group {
  
      public static final String AUDIO = "com.zzzmode.appopsx.permission-group.AUDIO";
      public static final String DEVICE = "com.zzzmode.appopsx.permission-group.DEVICE";
      public static final String OTHER = "com.zzzmode.appopsx.permission-group.OTHER";
      public static final String GPS = "com.zzzmode.appopsx.permission-group.GPS";
      public static final String CALLPHONE = "com.zzzmode.appopsx.permission-group.CALLPHONE";
      public static final String VIBRATION = "com.zzzmode.appopsx.permission-group.VIBRATION";
      public static final String NET = "com.zzzmode.appopsx.permission-group.NET";
      public static final String WIFI = "com.zzzmode.appopsx.permission-group.WIFI";
      public static final String NFC = "com.zzzmode.appopsx.permission-group.NFC";
      public static final String SETTINGS = "com.zzzmode.appopsx.permission-group.SETTINGS";
      public static final String NOTIFICATIONS = "com.zzzmode.appopsx.permission-group.NOTIFICATIONS";
      public static final String CLIPBOARD = "com.zzzmode.appopsx.permission-group.CLIPBOARD";
      public static final String FINGERPRINT = "com.zzzmode.appopsx.permission-group.FINGERPRINT";
  
    }
  
    private static final String[] RE_SORT_GROUPS =
        {
            permission_group.GPS,
            Manifest.permission_group.LOCATION,
            Manifest.permission_group.CALENDAR,
            Manifest.permission_group.SMS,
            Manifest.permission_group.CONTACTS,
            Manifest.permission_group.CAMERA,
            permission_group.CALLPHONE,
            Manifest.permission_group.PHONE,
            Manifest.permission_group.STORAGE,
            Manifest.permission_group.SENSORS,
            permission_group.FINGERPRINT,
            Manifest.permission_group.MICROPHONE,
            permission_group.VIBRATION,
            permission_group.NET,
            permission_group.WIFI,
            permission_group.NFC,
            permission_group.SETTINGS,
            permission_group.NOTIFICATIONS,
            permission_group.CLIPBOARD,
            permission_group.AUDIO,
            permission_group.DEVICE,
            permission_group.OTHER,
  
        };
  
    private static class PermGroupInfo {
  
      String title;
      String group;
      int icon;
  
      @Override
      public String toString() {
        return "PermGroupInfo{" +
            "title='" + title + '\'' +
            ", group='" + group + '\'' +
            '}';
      }
  
      public PermGroupInfo(String title, String group, int icon) {
        this.title = title;
        this.group = group;
        this.icon = icon;
      }
    }
  
    private static final SparseIntArray NO_PERM_OP = new SparseIntArray();
    private static final Map<String, String> FAKE_PERMS_GROUP = new HashMap<>();
  
    private static final Map<String, PermGroupInfo> PERMS_GROUPS = new HashMap<>();
  
    private static final PermGroupInfo OTHER_PERM_INFO = new PermGroupInfo(null,
        permission_group.OTHER, R.drawable.perm_group_other);
  
    static {
      int[] ops = {2, 11, 12, 15, 22, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 41, 42, 44, 45,
          46, 47, 48, 49, 50, 58, 61, 63, 65, 69};
      for (int op : ops) {
        NO_PERM_OP.put(op, op);
      }
  
      FAKE_PERMS_GROUP.put("COARSE_LOCATION", Manifest.permission_group.LOCATION);
      FAKE_PERMS_GROUP.put("FINE_LOCATION", permission_group.GPS);
      FAKE_PERMS_GROUP.put("GPS", permission_group.GPS);
      FAKE_PERMS_GROUP.put("NEIGHBORING_CELLS", Manifest.permission_group.LOCATION);
      FAKE_PERMS_GROUP.put("MONITOR_LOCATION", Manifest.permission_group.LOCATION);
      FAKE_PERMS_GROUP.put("MONITOR_HIGH_POWER_LOCATION", Manifest.permission_group.LOCATION);
  
      FAKE_PERMS_GROUP.put("READ_CALL_LOG", permission_group.CALLPHONE);
      FAKE_PERMS_GROUP.put("WRITE_CALL_LOG", permission_group.CALLPHONE);
      FAKE_PERMS_GROUP.put("CALL_PHONE", permission_group.CALLPHONE);
      FAKE_PERMS_GROUP.put("PROCESS_OUTGOING_CALLS", permission_group.CALLPHONE);
  
      FAKE_PERMS_GROUP.put("READ_SMS", Manifest.permission_group.SMS);
      FAKE_PERMS_GROUP.put("WRITE_SMS", Manifest.permission_group.SMS);
      FAKE_PERMS_GROUP.put("RECEIVE_SMS", Manifest.permission_group.SMS);
      FAKE_PERMS_GROUP.put("RECEIVE_EMERGECY_SMS", Manifest.permission_group.SMS);
      FAKE_PERMS_GROUP.put("RECEIVE_MMS", Manifest.permission_group.SMS);
      FAKE_PERMS_GROUP.put("RECEIVE_WAP_PUSH", Manifest.permission_group.SMS);
      FAKE_PERMS_GROUP.put("SEND_SMS", Manifest.permission_group.SMS);
      FAKE_PERMS_GROUP.put("READ_ICC_SMS", Manifest.permission_group.SMS);
      FAKE_PERMS_GROUP.put("WRITE_ICC_SMS", Manifest.permission_group.SMS);
  
      FAKE_PERMS_GROUP.put("PLAY_AUDIO", permission_group.AUDIO);
      FAKE_PERMS_GROUP.put("TAKE_MEDIA_BUTTONS", permission_group.AUDIO);
      FAKE_PERMS_GROUP.put("TAKE_AUDIO_FOCUS", permission_group.AUDIO);
      FAKE_PERMS_GROUP.put("AUDIO_MASTER_VOLUME", permission_group.AUDIO);
      FAKE_PERMS_GROUP.put("AUDIO_VOICE_VOLUME", permission_group.AUDIO);
      FAKE_PERMS_GROUP.put("AUDIO_RING_VOLUME", permission_group.AUDIO);
      FAKE_PERMS_GROUP.put("AUDIO_MEDIA_VOLUME", permission_group.AUDIO);
      FAKE_PERMS_GROUP.put("AUDIO_ALARM_VOLUME", permission_group.AUDIO);
      FAKE_PERMS_GROUP.put("AUDIO_NOTIFICATION_VOLUME", permission_group.AUDIO);
      FAKE_PERMS_GROUP.put("AUDIO_BLUETOOTH_VOLUME", permission_group.AUDIO);
  
      FAKE_PERMS_GROUP.put("VIBRATE", permission_group.DEVICE);
      FAKE_PERMS_GROUP.put("MUTE_MICROPHONE", permission_group.DEVICE);
      FAKE_PERMS_GROUP.put("TOAST_WINDOW", permission_group.DEVICE);
      FAKE_PERMS_GROUP.put("PROJECT_MEDIA", permission_group.DEVICE);
      FAKE_PERMS_GROUP.put("ACTIVATE_VPN", permission_group.DEVICE);
      FAKE_PERMS_GROUP.put("WRITE_WALLPAPER", permission_group.DEVICE);
      FAKE_PERMS_GROUP.put("ASSIST_STRUCTURE", permission_group.DEVICE);
      FAKE_PERMS_GROUP.put("ASSIST_SCREENSHOT", permission_group.DEVICE);
      FAKE_PERMS_GROUP.put("MOCK_LOCATION", permission_group.DEVICE);
      FAKE_PERMS_GROUP.put("TURN_ON_SCREEN", permission_group.DEVICE);
      FAKE_PERMS_GROUP.put("RUN_IN_BACKGROUND", permission_group.DEVICE);
  
      FAKE_PERMS_GROUP.put("ACCESS_PHONE_DATA", permission_group.NET);
      FAKE_PERMS_GROUP.put("ACCESS_WIFI_NETWORK", permission_group.NET);
  
      FAKE_PERMS_GROUP.put("VIBRATE", permission_group.VIBRATION);
  
      FAKE_PERMS_GROUP.put("WIFI_SCAN", permission_group.WIFI);
      FAKE_PERMS_GROUP.put("WIFI_CHANGE", permission_group.WIFI);
  
      FAKE_PERMS_GROUP.put("NFC_CHANGE", permission_group.NFC);
  
      FAKE_PERMS_GROUP.put("WRITE_SETTINGS", permission_group.SETTINGS);
  
      FAKE_PERMS_GROUP.put("ACCESS_NOTIFICATIONS", permission_group.NOTIFICATIONS);
      FAKE_PERMS_GROUP.put("POST_NOTIFICATION", permission_group.NOTIFICATIONS);
  
      FAKE_PERMS_GROUP.put("READ_CLIPBOARD", permission_group.CLIPBOARD);
      FAKE_PERMS_GROUP.put("WRITE_CLIPBOARD", permission_group.CLIPBOARD);
  
      FAKE_PERMS_GROUP.put("USE_FINGERPRINT", permission_group.FINGERPRINT);
  
      PERMS_GROUPS.put(Manifest.permission_group.CALENDAR,
          new PermGroupInfo(null, Manifest.permission_group.CALENDAR,
              R.drawable.perm_group_calendar));
      PERMS_GROUPS.put(Manifest.permission_group.CAMERA,
          new PermGroupInfo(null, Manifest.permission_group.CAMERA, R.drawable.perm_group_camera));
      PERMS_GROUPS.put(Manifest.permission_group.CONTACTS,
          new PermGroupInfo(null, Manifest.permission_group.CONTACTS,
              R.drawable.perm_group_contacts));
      PERMS_GROUPS.put(Manifest.permission_group.LOCATION,
          new PermGroupInfo(null, Manifest.permission_group.LOCATION,
              R.drawable.perm_group_location));
      PERMS_GROUPS.put(Manifest.permission_group.MICROPHONE,
          new PermGroupInfo(null, Manifest.permission_group.MICROPHONE,
              R.drawable.perm_group_microphone));
      PERMS_GROUPS.put(Manifest.permission_group.PHONE,
          new PermGroupInfo(null, Manifest.permission_group.PHONE, R.drawable.ic_perm_device_info));
      PERMS_GROUPS.put(Manifest.permission_group.SENSORS,
          new PermGroupInfo(null, Manifest.permission_group.SENSORS, R.drawable.perm_group_sensors));
      PERMS_GROUPS.put(Manifest.permission_group.SMS,
          new PermGroupInfo(null, Manifest.permission_group.SMS, R.drawable.perm_group_sms));
      PERMS_GROUPS.put(Manifest.permission_group.STORAGE,
          new PermGroupInfo(null, Manifest.permission_group.STORAGE, R.drawable.perm_group_storage));
  
      PERMS_GROUPS.put(permission_group.AUDIO,
          new PermGroupInfo(null, permission_group.AUDIO, R.drawable.perm_group_audio));
      PERMS_GROUPS.put(permission_group.DEVICE,
          new PermGroupInfo(null, permission_group.DEVICE, R.drawable.perm_group_device));
      PERMS_GROUPS.put(permission_group.OTHER,
          new PermGroupInfo(null, permission_group.OTHER, R.drawable.perm_group_other));
      PERMS_GROUPS.put(permission_group.GPS,
          new PermGroupInfo(null, permission_group.GPS, R.drawable.perm_group_gps));
      PERMS_GROUPS.put(permission_group.CALLPHONE,
          new PermGroupInfo(null, permission_group.CALLPHONE, R.drawable.perm_group_callphone));
      PERMS_GROUPS.put(permission_group.VIBRATION,
          new PermGroupInfo(null, permission_group.VIBRATION, R.drawable.perm_group_vibration));
      PERMS_GROUPS.put(permission_group.NET,
          new PermGroupInfo(null, permission_group.NET, R.drawable.perm_group_net));
      PERMS_GROUPS.put(permission_group.WIFI,
          new PermGroupInfo(null, permission_group.WIFI, R.drawable.perm_group_wifi));
      PERMS_GROUPS.put(permission_group.NFC,
          new PermGroupInfo(null, permission_group.NFC, R.drawable.perm_group_nfc));
      PERMS_GROUPS.put(permission_group.SETTINGS,
          new PermGroupInfo(null, permission_group.SETTINGS, R.drawable.perm_group_settings));
      PERMS_GROUPS.put(permission_group.NOTIFICATIONS,
          new PermGroupInfo(null, permission_group.NOTIFICATIONS,
              R.drawable.perm_group_notifications));
      PERMS_GROUPS.put(permission_group.CLIPBOARD,
          new PermGroupInfo(null, permission_group.CLIPBOARD, R.drawable.perm_group_clipboard));
  
      PERMS_GROUPS.put(permission_group.FINGERPRINT,
          new PermGroupInfo(null, permission_group.FINGERPRINT, R.drawable.perm_group_fingerprint));
    }
  
    private static final String TAG = "Helper";
  
    private static final Map<String, Integer> sPermI18N = new HashMap<String, Integer>() {{
      put("POST_NOTIFICATION", R.string.permlab_POST_NOTIFICATION);
      put("READ_CLIPBOARD", R.string.permlab_READ_CLIPBOARD);
      put("WRITE_CLIPBOARD", R.string.permlab_WRITE_CLIPBOARD);
      put("TURN_ON_SCREEN", R.string.permlab_TURN_ON_SCREEN);
      put("RUN_IN_BACKGROUND", R.string.permlab_RUN_IN_BACKGROUND);
      put("MONITOR_LOCATION", R.string.permlab_MONITOR_LOCATION);
      put("MONITOR_HIGH_POWER_LOCATION", R.string.permlab_MONITOR_HIGH_POWER_LOCATION);
      put("NEIGHBORING_CELLS", R.string.permlab_NEIGHBORING_CELLS);
      put("PLAY_AUDIO", R.string.permlab_PLAY_AUDIO);
      put("AUDIO_MASTER_VOLUME", R.string.permlab_AUDIO_MASTER_VOLUME);
      put("AUDIO_VOICE_VOLUME", R.string.permlab_AUDIO_VOICE_VOLUME);
      put("AUDIO_RING_VOLUME", R.string.permlab_AUDIO_RING_VOLUME);
      put("AUDIO_MEDIA_VOLUME", R.string.permlab_AUDIO_MEDIA_VOLUME);
      put("AUDIO_ALARM_VOLUME", R.string.permlab_AUDIO_ALARM_VOLUME);
      put("AUDIO_NOTIFICATION_VOLUME", R.string.permlab_AUDIO_NOTIFICATION_VOLUME);
      put("AUDIO_BLUETOOTH_VOLUME", R.string.permlab_AUDIO_BLUETOOTH_VOLUME);
      put("TOAST_WINDOW", R.string.permlab_TOAST_WINDOW);
      put("ACTIVATE_VPN", R.string.permlab_ACTIVATE_VPN);
      put("TAKE_AUDIO_FOCUS", R.string.permlab_TAKE_AUDIO_FOCUS);
      put("ACCESS_PHONE_DATA", R.string.permlab_ACCESS_MOBLIE_NETWORK_DATA);
      put("ACCESS_WIFI_NETWORK", R.string.permlab_ACCESS_WIFI_NETWORK_DATA);
  
    }};
  
  
    public static void updataShortcuts(final Context context) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
        getInstalledApps(context, false)
            .concatMap(new Function<List<AppInfo>, ObservableSource<AppInfo>>() {
              @Override
              public ObservableSource<AppInfo> apply(List<AppInfo> appInfos) throws Exception {
                return Observable.fromIterable(appInfos);
              }
            }).filter(new Predicate<AppInfo>() {
          @Override
          public boolean test(AppInfo info) throws Exception {
            return !BuildConfig.APPLICATION_ID.equals(info.packageName);
          }
        }).collect(new Callable<List<AppInfo>>() {
          @Override
          public List<AppInfo> call() throws Exception {
            return new ArrayList<AppInfo>();
          }
        }, new BiConsumer<List<AppInfo>, AppInfo>() {
          @Override
          public void accept(List<AppInfo> appInfos, AppInfo info) throws Exception {
            appInfos.add(info);
          }
        }).map(new Function<List<AppInfo>, List<AppInfo>>() {
          @Override
          public List<AppInfo> apply(List<AppInfo> appInfos) throws Exception {
            Collections.sort(appInfos, new Comparator<AppInfo>() {
              @Override
              public int compare(AppInfo o1, AppInfo o2) {
                return o1.time > o2.time ? -1 : 1;
              }
            });
            return appInfos;
          }
        }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
            .subscribe(new ResourceSingleObserver<List<AppInfo>>() {
              @Override
              public void onSuccess(List<AppInfo> value) {
                try {
                  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
                    updataShortcuts(context, value);
                  }
                } catch (Exception e) {
                  e.printStackTrace();
                }
              }
  
              @Override
              public void onError(Throwable e) {
  
              }
            });
  
      }
    }
  
    @RequiresApi(api = Build.VERSION_CODES.N_MR1)
    private static void updataShortcuts(Context context, List<AppInfo> items) {
      ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
      List<ShortcutInfo> shortcutInfoList = new ArrayList<>();
      int max = shortcutManager.getMaxShortcutCountPerActivity();
      for (int i = 0; i < max && i < items.size(); i++) {
        AppInfo appInfo = items.get(i);
        ShortcutInfo.Builder shortcut = new ShortcutInfo.Builder(context, appInfo.packageName);
        shortcut.setShortLabel(appInfo.appName);
        shortcut.setLongLabel(appInfo.appName);
  
        shortcut.setIcon(
            Icon.createWithBitmap(drawableToBitmap(LocalImageLoader.getDrawable(context, appInfo))));
  
        Intent intent = new Intent(context, AppPermissionActivity.class);
        intent.putExtra(AppPermissionActivity.EXTRA_APP_PKGNAME, appInfo.packageName);
        intent.putExtra(AppPermissionActivity.EXTRA_APP_NAME, appInfo.appName);
        intent.setAction(Intent.ACTION_DEFAULT);
        shortcut.setIntent(intent);
  
        shortcutInfoList.add(shortcut.build());
      }
      shortcutManager.setDynamicShortcuts(shortcutInfoList);
    }
  
    private static Bitmap drawableToBitmap(Drawable drawable) {
      Bitmap bitmap = null;
  
      if (drawable instanceof BitmapDrawable) {
        BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
        if (bitmapDrawable.getBitmap() != null) {
          return bitmapDrawable.getBitmap();
        }
      }
  
      if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
        bitmap = Bitmap.createBitmap(1, 1,
            Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel
      } else {
        bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
            Bitmap.Config.ARGB_8888);
      }
  
      Canvas canvas = new Canvas(bitmap);
      drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
      drawable.draw(canvas);
      return bitmap;
    }
  
  
    public static Single<AppInfo> getAppInfo(final Context context, String pkgName) {
      return SingleJust.just(pkgName).map(new Function<String, AppInfo>() {
        @Override
        public AppInfo apply(String s) throws Exception {
          PackageManager packageManager = context.getPackageManager();
          PackageInfo packageInfo = packageManager.getPackageInfo(s, 0);
  
          AppInfo info = new AppInfo();
          info.packageName = packageInfo.packageName;
          info.appName = BidiFormatter.getInstance()
              .unicodeWrap(packageInfo.applicationInfo.loadLabel(packageManager)).toString();
          info.time = Math.max(packageInfo.lastUpdateTime, packageInfo.firstInstallTime);
          info.installTime = packageInfo.firstInstallTime;
          info.updateTime = packageInfo.lastUpdateTime;
          info.applicationInfo = packageInfo.applicationInfo;
  
          LocalImageLoader.initAdd(context, info);
          return info;
        }
      });
  
    }
  
    public static Observable<List<AppInfo>> getInstalledApps(final Context context,
        final boolean loadSysapp) {
  
      return Observable.create(new ObservableOnSubscribe<List<AppInfo>>() {
        @Override
        public void subscribe(final ObservableEmitter<List<AppInfo>> e) throws Exception {
          PackageManager packageManager = context.getPackageManager();
          int uid = Users.getInstance().getCurrentUid();
          List<PackageInfo> installedPackages = null;
          if(uid == 0){
            installedPackages = packageManager.getInstalledPackages(0);
          }else {
            installedPackages = AppOpsx.getInstance(context).getApiSupporter().getInstalledPackages(0,uid);
          }
  
  
          List<AppInfo> zhAppInfos = new ArrayList<AppInfo>();
          List<AppInfo> enAppInfos = new ArrayList<AppInfo>();
          for (PackageInfo installedPackage : installedPackages) {
            if (loadSysapp
                || (installedPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
              AppInfo info = new AppInfo();
              info.packageName = installedPackage.packageName;
              info.appName = BidiFormatter.getInstance()
                  .unicodeWrap(installedPackage.applicationInfo.loadLabel(packageManager)).toString();
              info.time = Math
                  .max(installedPackage.lastUpdateTime, installedPackage.firstInstallTime);
              info.installTime = installedPackage.firstInstallTime;
              info.updateTime = installedPackage.lastUpdateTime;
              info.applicationInfo = installedPackage.applicationInfo;
  
              LocalImageLoader.initAdd(context, info);
  
              //some of the app name is empty.
              if(TextUtils.isEmpty(info.appName)){
                info.appName = info.packageName;
              }
              final char c = info.appName.charAt(0);
              if (c >= 48 && c <= 122) {
                enAppInfos.add(info);
              } else {
                zhAppInfos.add(info);
              }
  
            }
          }
  
          Collections.sort(enAppInfos, new Comparator<AppInfo>() {
            @Override
            public int compare(AppInfo o1, AppInfo o2) {
              return o1.appName.compareToIgnoreCase(o2.appName);
            }
          });
  
          Collections.sort(zhAppInfos, new Comparator<AppInfo>() {
            @Override
            public int compare(AppInfo o1, AppInfo o2) {
              return o2.appName.compareTo(o1.appName);
            }
          });
          List<AppInfo> ret = new ArrayList<AppInfo>();
  
          int type = PreferenceManager.getDefaultSharedPreferences(context)
              .getInt("pref_app_sort_type", 0);
          if (type == 1) {
            //按名称排序[字母在后]
  
            ret.addAll(zhAppInfos);
            ret.addAll(enAppInfos);
          } else {
            //按名称排序[字母在前] 默认
            ret.addAll(enAppInfos);
            ret.addAll(zhAppInfos);
          }
  
          e.onNext(ret);
          e.onComplete();
        }
      });
    }
  
  
    public static Observable<PreAppInfo> getAppsPermission(final Context context,
        AppInfo[] appInfos) {
      return Observable.fromArray(appInfos).map(new Function<AppInfo, PreAppInfo>() {
        @Override
        public PreAppInfo apply(@NonNull AppInfo s) throws Exception {
          OpsResult opsForPackage = AppOpsx.getInstance(context).getOpsForPackage(s.packageName);
          if (opsForPackage != null) {
            if (opsForPackage.getException() != null) {
              throw new Exception(opsForPackage.getException());
            }
          } else {
            throw new NullPointerException("getOpsForPackage:" + s + " return null !");
          }
          PreAppInfo appInfo = new PreAppInfo(s.packageName);
          List<PackageOps> opses = opsForPackage.getList();
          if (opses != null) {
            StringBuilder sb = new StringBuilder();
            for (PackageOps opse : opses) {
              List<OpEntry> ops = opse.getOps();
  
              if (ops != null) {
                for (OpEntry op : ops) {
                  if (op.getMode() == AppOpsManager.MODE_IGNORED) {
                    sb.append(op.getOp()).append(',');
                  }
                }
              }
            }
            int len = sb.length();
            if (len > 0 && sb.charAt(len - 1) == ',') {
              sb.deleteCharAt(len - 1);
            }
            appInfo.setIgnoredOps(sb.toString());
            return appInfo;
          } else {
            throw new NullPointerException("");
          }
        }
      })
          .retry(5, new Predicate<Throwable>() {
            @Override
            public boolean test(Throwable throwable) throws Exception {
              return throwable instanceof IOException || throwable instanceof NullPointerException;
            }
          })
          .subscribeOn(Schedulers.io());
    }
  
    public static Observable<List<OpEntryInfo>> getAppPermission(final Context context,
        final String packageName) {
      return getAppPermission(context, packageName, false);
    }
  
  
    public static Observable<List<OpEntryInfo>> getAppPermission(final Context context,
        final String packageName, final boolean needNoPermsOp) {
      return Observable.create(new ObservableOnSubscribe<OpsResult>() {
        @Override
        public void subscribe(ObservableEmitter<OpsResult> e) throws Exception {
  
          OpsResult opsForPackage = AppOpsx.getInstance(context).getOpsForPackage(packageName);
          if (opsForPackage != null) {
            if (opsForPackage.getException() == null) {
              e.onNext(opsForPackage);
            } else {
              throw new Exception(opsForPackage.getException());
            }
          }
          e.onComplete();
  
        }
      })
          .retry(5, new Predicate<Throwable>() {
            @Override
            public boolean test(Throwable throwable) throws Exception {
              return throwable instanceof IOException || throwable instanceof NullPointerException;
            }
          })
          .subscribeOn(Schedulers.io()).map(opsResult2OpEntryInfoMap(context, needNoPermsOp)).map(new Function<List<OpEntryInfo>, List<OpEntryInfo>>() {
            @Override
            public List<OpEntryInfo> apply(@NonNull List<OpEntryInfo> opEntryInfos) throws Exception {
              return sortPermsFunction(context, opEntryInfos);
            }
          });
    }
  
    private static Function<OpsResult, List<OpEntryInfo>> opsResult2OpEntryInfoMap(final Context context, final boolean needNoPermsOp) {
      return new Function<OpsResult, List<OpEntryInfo>>() {
        @Override
        public List<OpEntryInfo> apply(OpsResult opsResult) throws Exception {
          List<PackageOps> opses = opsResult.getList();
          if (opses != null) {
            List<OpEntryInfo> list = new ArrayList<OpEntryInfo>();
            PackageManager pm = context.getPackageManager();
            for (PackageOps opse : opses) {
              List<OpEntry> ops = opse.getOps();
  
              if (ops != null) {
                SparseIntArray hasOp = new SparseIntArray();
                for (OpEntry op : ops) {
                  OpEntryInfo opEntryInfo = opEntry2Info(op, context, pm);
                  if (opEntryInfo != null) {
                    hasOp.put(op.getOp(), op.getOp());
                    list.add(opEntryInfo);
                  }
                }
  
                if (needNoPermsOp) {
                  int size = NO_PERM_OP.size();
                  for (int i = 0; i < size; i++) {
                    int opk = NO_PERM_OP.keyAt(i);
                    if (hasOp.indexOfKey(opk) < 0) {
                      OpEntry op = new OpEntry(opk, AppOpsManager.MODE_ALLOWED, 0, 0, 0, 0, null);
                      OpEntryInfo opEntryInfo = opEntry2Info(op, context, pm);
                      if (opEntryInfo != null) {
                        list.add(opEntryInfo);
                      }
                    }
                  }
                }
              }
            }
            return list;
          }
          return Collections.emptyList();
        }
      };
    }
  
    private static OpEntryInfo opEntry2Info(OpEntry op, Context context, PackageManager pm) {
      OpEntryInfo opEntryInfo = new OpEntryInfo(op);
      if (OtherOp.isOtherOp(op.getOp())) {
        opEntryInfo.opName = OtherOp.getOpName(op.getOp());
        opEntryInfo.opPermsName = OtherOp.getOpPermName(op.getOp());
      }
      if (opEntryInfo.opName != null) {
        try {
          if (!OtherOp.isOtherOp(op.getOp())) {
            PermissionInfo permissionInfo = pm.getPermissionInfo(opEntryInfo.opPermsName, 0);
            opEntryInfo.opPermsLab = String.valueOf(permissionInfo.loadLabel(pm));
            opEntryInfo.opPermsDesc = String.valueOf(permissionInfo.loadDescription(pm));
          }
        } catch (PackageManager.NameNotFoundException e) {
          //ignore
        }
  
        if (opEntryInfo.opPermsLab == null) {
          Integer resId = sPermI18N.get(opEntryInfo.opName);
          if (resId != null) {
            opEntryInfo.opPermsLab = context.getString(resId);
            opEntryInfo.opPermsDesc = opEntryInfo.opName;
          }
        }
  
        return opEntryInfo;
      }
      return null;
    }
  
  
    static Observable<AppPermissions> getAllAppPermissions(final Context context,
        final boolean loadSysapp, final boolean reqNet){
      return Observable.create(new ObservableOnSubscribe<OpsResult>() {
        @Override
        public void subscribe(ObservableEmitter<OpsResult> e) throws Exception {
  
          OpsResult opsForPackage = AppOpsx.getInstance(context).getPackagesForOps(null,reqNet);
          if (opsForPackage != null) {
            if (opsForPackage.getException() == null) {
              e.onNext(opsForPackage);
            } else {
              throw new Exception(opsForPackage.getException());
            }
          }
          e.onComplete();
        }
      })
          .retry(5, new Predicate<Throwable>() {
            @Override
            public boolean test(Throwable throwable) throws Exception {
              return throwable instanceof IOException || throwable instanceof NullPointerException;
            }
          }).map(new Function<OpsResult, Map<String,PackageOps>>() {
            @Override
            public Map<String,PackageOps> apply(OpsResult result) throws Exception {
              Map<String,PackageOps> map = new HashMap<>();
              List<PackageOps> list = result.getList();
              if(list != null){
                for (PackageOps packageOps : list) {
                  map.put(packageOps.getPackageName(),packageOps);
                }
              }
              return map;
            }
          }).flatMap(new Function<Map<String, PackageOps>, ObservableSource<List<AppPermissions>>>() {
  
            public ObservableSource<List<AppPermissions>> apply(final Map<String, PackageOps> result) throws Exception {
              return getInstalledApps(context,loadSysapp).map(
                  new Function<List<AppInfo>, List<AppPermissions>>() {
                    @Override
                    public List<AppPermissions> apply(List<AppInfo> appInfos) throws Exception {
                      List<AppPermissions> list = new ArrayList<AppPermissions>();
                      PackageManager pm = context.getPackageManager();
                      if(appInfos != null){
                        for (AppInfo appInfo : appInfos) {
                          AppPermissions p = new AppPermissions();
                          p.appInfo = appInfo;
  
                          PackageOps packageOps = result.get(appInfo.packageName);
                          if(packageOps != null) {
                            List<OpEntry> ops = packageOps.getOps();
                            if (ops != null) {
                              List<OpEntryInfo> opEntryInfos = new ArrayList<>();
                              SparseIntArray hasOp = new SparseIntArray();
                              for (OpEntry op : ops) {
                                OpEntryInfo opEntryInfo = opEntry2Info(op, context, pm);
                                if (opEntryInfo != null) {
                                  hasOp.put(op.getOp(), op.getOp());
                                  opEntryInfos.add(opEntryInfo);
                                }
                              }
                              p.opEntries = opEntryInfos;
                              list.add(p);
                            }
                          }
                        }
                      }
                      return list;
                    }
                  });
            }
          }).flatMap(new Function<List<AppPermissions>, ObservableSource<AppPermissions>>() {
            @Override
            public ObservableSource<AppPermissions> apply(List<AppPermissions> appPermissions) throws Exception {
              return Observable.fromIterable(appPermissions);
            }
          });
    }
  
  
    public static Single<List<Pair<AppInfo, OpEntryInfo>>> getPermsUsageStatus(final Context context,
        final boolean loadSysapp){
      return getAllAppPermissions(context,loadSysapp,false)
          .collect(new Callable<List<Pair<AppInfo, OpEntryInfo>>>() {
            @Override
            public List<Pair<AppInfo, OpEntryInfo>> call() throws Exception {
              return new ArrayList<Pair<AppInfo, OpEntryInfo>>();
            }
          }, new BiConsumer<List<Pair<AppInfo, OpEntryInfo>>, AppPermissions>() {
            @Override
            public void accept(List<Pair<AppInfo, OpEntryInfo>> pairs, AppPermissions appPermissions)
                throws Exception {
              if(appPermissions.opEntries != null){
                for (OpEntryInfo opEntry : appPermissions.opEntries) {
                  //被调用过并且允许的才加入列表
                  //超过一个月的记录不显示
                  long time = opEntry.opEntry.getTime();
                  long now = System.currentTimeMillis();
  
                  if(time > 0 && (now-time < 60 * 60 * 24 * 31 * 1000L) && opEntry.isAllowed()) {
                    joinOpEntryInfo(opEntry,context);
                    pairs.add(Pair.create(appPermissions.appInfo, opEntry));
                  }
                }
              }
            }
          }).flatMapObservable(new Function<List<Pair<AppInfo, OpEntryInfo>>, ObservableSource<Pair<AppInfo, OpEntryInfo>>>() {
            @Override
            public ObservableSource<Pair<AppInfo, OpEntryInfo>> apply(@NonNull List<Pair<AppInfo, OpEntryInfo>> pairs)
                throws Exception {
              return Observable.fromIterable(pairs);
            }
          }).toSortedList(new Comparator<Pair<AppInfo, OpEntryInfo>>() {
            @Override
            public int compare(Pair<AppInfo, OpEntryInfo> t0,
                Pair<AppInfo, OpEntryInfo> t1) {
              return Long.compare(t1.second.opEntry.getTime(),t0.second.opEntry.getTime());
            }
          });
    }
  
    public static Single<List<PermissionGroup>> getPermissionGroup(final Context context,
        final boolean loadSysapp, final boolean reqNet,final boolean showIgnored) {
      return getAllAppPermissions(context, loadSysapp, reqNet)
          .collect(new Callable<Map<String, List<AppPermissions>>>() {
            @Override
            public Map<String, List<AppPermissions>> call() throws Exception {
              return new HashMap<String, List<AppPermissions>>();
            }
          }, new BiConsumer<Map<String, List<AppPermissions>>, AppPermissions>() {
            @Override
            public void accept(Map<String, List<AppPermissions>> map, AppPermissions app)
                throws Exception {
              if (app.opEntries != null && app.hasPermissions()) {
                for (OpEntryInfo opEntry : app.opEntries) {
                  if (opEntry.opName != null) {
                    List<AppPermissions> appPermissionses = map.get(opEntry.opName);
                    if (appPermissionses == null) {
                      appPermissionses = new ArrayList<AppPermissions>();
                    }
                    appPermissionses.add(app);
                    map.put(opEntry.opName, appPermissionses);
                  }
                }
              }
            }
          })
          .map(new Function<Map<String, List<AppPermissions>>, List<PermissionGroup>>() {
            @Override
            public List<PermissionGroup> apply(Map<String, List<AppPermissions>> map)
                throws Exception {
              List<PermissionGroup> groups = new ArrayList<PermissionGroup>();
              Set<Entry<String, List<AppPermissions>>> entries = map.entrySet();
              for (Entry<String, List<AppPermissions>> entry : entries) {
                PermissionGroup group = new PermissionGroup();
                group.opName = entry.getKey();
  
                List<AppPermissions> value = entry.getValue();
  
                group.count = value.size();
                group.apps = new ArrayList<PermissionChildItem>();
  
                for (AppPermissions appPermissions : value) {
                  PermissionChildItem item = new PermissionChildItem();
                  item.appInfo = appPermissions.appInfo;
  
  
                  boolean skip = false;
                  if (appPermissions.opEntries != null) {
                    for (OpEntryInfo opEntry : appPermissions.opEntries) {
                      if (group.opName.equals(opEntry.opName)) {
                        item.opEntryInfo = opEntry;
                        if (opEntry.opEntry.getMode() == AppOpsManager.MODE_ALLOWED) {
                          group.grants += 1;
                        }else if(!showIgnored){
                          skip = true;
                        }
                        group.opPermsName = opEntry.opPermsName;
                        group.opPermsDesc = opEntry.opPermsDesc;
                        group.opPermsLab = opEntry.opPermsLab;
                        break;
                      }
                    }
                  }
  
                  if(!skip){
                    group.apps.add(item);
                  }
  
                }
  
  
                Collections.sort(group.apps, new Comparator<PermissionChildItem>() {
                  @Override
                  public int compare(PermissionChildItem o1, PermissionChildItem o2) {
                    return Long.compare(o2.opEntryInfo.opEntry.getTime(),o1.opEntryInfo.opEntry.getTime());
                  }
                });
  
                groups.add(group);
              }
  
              return groups;
            }
          }).map(new Function<List<PermissionGroup>, List<PermissionGroup>>() {
            @Override
            public List<PermissionGroup> apply(List<PermissionGroup> permissionGroups)
                throws Exception {
              Map<String, List<PermissionGroup>> groups = new HashMap<String, List<PermissionGroup>>();
              PackageManager pm = context.getPackageManager();
              for (PermissionGroup permissionGroup : permissionGroups) {
  
                String groupS = FAKE_PERMS_GROUP.get(permissionGroup.opName);
  
                if (groupS == null && permissionGroup.opPermsName != null) {
                  try {
                    PermissionInfo permissionInfo = pm
                        .getPermissionInfo(permissionGroup.opPermsName, PackageManager.GET_META_DATA);
                    groupS = permissionInfo.group;
                  } catch (Exception e) {
                    //ignore
                  }
                }
  
                PermGroupInfo permGroupInfo = null;
                if (groupS != null) {
                  permGroupInfo = PERMS_GROUPS.get(groupS);
                }
                if (permGroupInfo == null) {
                  permGroupInfo = OTHER_PERM_INFO;
                }
                permissionGroup.icon = permGroupInfo.icon;
                permissionGroup.group = permGroupInfo.group;
  
                List<PermissionGroup> value = groups.get(permissionGroup.group);
                if (value == null) {
                  value = new ArrayList<PermissionGroup>();
                }
                value.add(permissionGroup);
  
                groups.put(permissionGroup.group, value);
              }
  
              return reSort(RE_SORT_GROUPS, groups);
            }
          });
    }
  
  
    private static List<PermissionGroup> reSort(String[] groupNames,
        Map<String, List<PermissionGroup>> groups) {
      List<PermissionGroup> ret = new LinkedList<PermissionGroup>();
      for (String groupName : groupNames) {
        List<PermissionGroup> permissionGroups = groups.get(groupName);
        if (permissionGroups != null) {
          ret.addAll(permissionGroups);
        }
      }
      return ret;
    }
  
  
    public static Observable<OpsResult> setMode(final Context context, final String pkgName,
        final OpEntryInfo opEntryInfo,boolean isAllow) {
      if (isAllow) {
        opEntryInfo.mode = AppOpsManager.MODE_ALLOWED;
      } else {
        opEntryInfo.mode = AppOpsManager.MODE_IGNORED;
      }
      Map<String, String> map = new HashMap<String, String>(2);
      map.put("new_mode", String.valueOf(opEntryInfo.mode));
      map.put("op_name", opEntryInfo.opName);
      return setMode(context, pkgName, opEntryInfo);
    }
  
    public static Observable<OpsResult> setMode(final Context context, final String pkgName,
        final OpEntryInfo opEntryInfo) {
  
      return Observable.create(new ObservableOnSubscribe<OpsResult>() {
        @Override
        public void subscribe(ObservableEmitter<OpsResult> e) throws Exception {
  
          OpsResult opsForPackage = AppOpsx.getInstance(context)
              .setOpsMode(pkgName, opEntryInfo.opEntry.getOp(), opEntryInfo.mode);
          if (opsForPackage != null) {
            if (opsForPackage.getException() == null) {
              e.onNext(opsForPackage);
            } else {
              throw new Exception(opsForPackage.getException());
            }
          }
          e.onComplete();
  
        }
      }).retry(5, new Predicate<Throwable>() {
        @Override
        public boolean test(Throwable throwable) throws Exception {
          return throwable instanceof IOException || throwable instanceof NullPointerException;
        }
      });
    }
  
  
    public static Observable<OpsResult> setModes(final Context context, final String pkgName,
        final int opMode, List<Integer> ops) {
      return Observable.fromIterable(ops)
          .flatMap(new Function<Integer, ObservableSource<OpsResult>>() {
            @Override
            public ObservableSource<OpsResult> apply(@NonNull Integer integer) throws Exception {
              return Observable.just(integer).map(new Function<Integer, OpsResult>() {
                @Override
                public OpsResult apply(@NonNull Integer integer) throws Exception {
                  OpsResult opsForPackage = AppOpsx.getInstance(context)
                      .setOpsMode(pkgName, integer, opMode);
                  if (opsForPackage != null) {
                    if (opsForPackage.getException() == null) {
                      return opsForPackage;
                    } else {
                      throw new Exception(opsForPackage.getException());
                    }
                  }
                  return null;
                }
              }).retry(5, new Predicate<Throwable>() {
                @Override
                public boolean test(Throwable throwable) throws Exception {
                  return throwable instanceof IOException
                      || throwable instanceof NullPointerException;
                }
              });
            }
          });
    }
  
    public static Single<OpsResult> resetMode(final Context context, final String pkgName) {
      return SingleJust.just(pkgName).map(new Function<String, OpsResult>() {
        @Override
        public OpsResult apply(@NonNull String s) throws Exception {
          OpsResult opsForPackage = AppOpsx.getInstance(context).resetAllModes(pkgName);
          if (opsForPackage != null && opsForPackage.getException() != null) {
            throw new Exception(opsForPackage.getException());
          }
          return opsForPackage;
        }
      });
    }
  
    public static Single<SparseIntArray> autoDisable(final Context context, String pkg) {
  
      return SingleJust.just(pkg).map(new Function<String, SparseIntArray>() {
        @Override
        public SparseIntArray apply(String s) throws Exception {
  
          List<OpEntryInfo> opEntryInfos = getAppPermission(context, s).blockingFirst();
  
          SparseIntArray canIgnored = new SparseIntArray();//可以忽略的op
          if (opEntryInfos != null && !opEntryInfos.isEmpty()) {
            for (OpEntryInfo opEntryInfo : opEntryInfos) {
              int op = opEntryInfo.opEntry.getOp();
              canIgnored.put(op, op);
            }
          }
  
          SparseIntArray list = new SparseIntArray();
          SparseIntArray allowedIgnoreOps = getAllowedIgnoreOps(context);
  
          if (allowedIgnoreOps != null && allowedIgnoreOps.size() > 0) {
            int size = allowedIgnoreOps.size();
            for (int i = 0; i < size; i++) {
              int op = allowedIgnoreOps.keyAt(i);
              if (canIgnored.indexOfKey(op) >= 0 || NO_PERM_OP.indexOfKey(op) >= 0) {
                //
                list.put(op, op);
              }
            }
          }
          for (int i = 0; i < list.size(); i++) {
            try {
              int op = list.keyAt(i);
              AppOpsx.getInstance(context).setOpsMode(s, op, AppOpsManager.MODE_IGNORED);
            } catch (Exception ee) {
              ee.printStackTrace();
            }
          }
          return list;
        }
      });
  //        return SingleJust.create(new SingleOnSubscribe<SparseIntArray>() {
  //            @Override
  //            public void subscribe(SingleEmitter<SparseIntArray> e) throws Exception {
  //                List<OpEntryInfo> opEntryInfos = getAppPermission(context, pkg).blockingFirst();
  //
  //                SparseIntArray canIgnored = new SparseIntArray();//可以忽略的op
  //                if (opEntryInfos != null && !opEntryInfos.isEmpty()) {
  //                    for (OpEntryInfo opEntryInfo : opEntryInfos) {
  //                        int op = opEntryInfo.opEntry.getOp();
  //                        canIgnored.put(op, op);
  //                    }
  //                }
  //
  //
  //                SparseIntArray list = new SparseIntArray();
  //                SparseIntArray allowedIgnoreOps = getAllowedIgnoreOps(context);
  //
  //                if (allowedIgnoreOps != null && allowedIgnoreOps.size() > 0) {
  //                    int size = allowedIgnoreOps.size();
  //                    for (int i = 0; i < size; i++) {
  //                        int op = allowedIgnoreOps.keyAt(i);
  //                        if (canIgnored.indexOfKey(op) >= 0 || NO_PERM_OP.indexOfKey(op) >= 0) {
  //                            //
  //                            list.put(op, op);
  //                        }
  //                    }
  //                }
  //                for (int i = 0; i < list.size(); i++) {
  //                    try {
  //                        int op = list.keyAt(i);
  //                        AppOpsx.getInstance(context).setOpsMode(pkg, op, AppOpsManager.MODE_IGNORED);
  //                    } catch (Exception ee) {
  //                        ee.printStackTrace();
  //                    }
  //                }
  //                e.onSuccess(list);
  //            }
  //        });
    }
  
  
    private static final SparseArray<OpEntryInfo> sOpEntryInfo = new SparseArray<>();
    private static final SparseIntArray sAllOps = new SparseIntArray();
    private static final List<OpEntryInfo> sOpEntryInfoList = new ArrayList<>();
  
    public static List<OpEntryInfo> getLocalOpEntryInfos(Context context) {
      if (sOpEntryInfoList.isEmpty()) {
        int[] sOpToSwitch = FixCompat.sOpToSwitch();
        String[] sOpNames = FixCompat.sOpNames();
        String[] sOpPerms = FixCompat.sOpPerms();
        int len = sOpPerms.length;
        PackageManager pm = context.getPackageManager();
        for (int i = 0; i < len; i++) {
          OpEntry entry = new OpEntry(sOpToSwitch[i], AppOpsManager.MODE_ALLOWED, 0, 0, 0, 0, null);
          OpEntryInfo opEntryInfo = new OpEntryInfo(entry);
          opEntryInfo.opName = sOpNames[i];
          try {
            PermissionInfo permissionInfo = pm.getPermissionInfo(sOpPerms[i], 0);
            opEntryInfo.opPermsLab = String.valueOf(permissionInfo.loadLabel(pm));
            opEntryInfo.opPermsDesc = String.valueOf(permissionInfo.loadDescription(pm));
          } catch (PackageManager.NameNotFoundException e) {
            //ignore
            Integer resId = sPermI18N.get(opEntryInfo.opName);
            if (resId != null) {
              opEntryInfo.opPermsLab = context.getString(resId);
              opEntryInfo.opPermsDesc = opEntryInfo.opName;
            } else {
              opEntryInfo.opPermsLab = opEntryInfo.opName;
            }
          }
          sOpEntryInfo.put(entry.getOp(), opEntryInfo);
          sAllOps.put(entry.getOp(), entry.getOp());
          sOpEntryInfoList.add(opEntryInfo);
        }
      }
      return new ArrayList<OpEntryInfo>(sOpEntryInfoList);
    }
  
  
    public static SparseIntArray getAllowedIgnoreOps(Context context) {
      SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
      String result = sp.getString("auto_perm_templete", context.getString(R.string.default_ignored));
      SparseIntArray ret = new SparseIntArray();
      String[] split = result.split(",");
      for (String s : split) {
        try {
          int op = Integer.parseInt(s);
          ret.put(op, op);
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
      return ret;
    }
  
    public static Single<Boolean> closeBgServer(Context context) {
      return SingleJust.just(context).map(new Function<Context, Boolean>() {
        @Override
        public Boolean apply(@NonNull Context context) throws Exception {
          AppOpsx.getInstance(context).closeBgServer();
          return true;
        }
      });
    }
  
    public static Single<Boolean> restartServer(Context context) {
      return SingleJust.just(context).map(new Function<Context, Boolean>() {
        @Override
        public Boolean apply(@NonNull Context context) throws Exception {
          AppOpsx.getInstance(context).getApiSupporter().restartServer(context);
          return true;
        }
      });
    }
  
    public static Function<List<AppInfo>, List<AppInfo>> getSortComparator(final Context context) {
      return new Function<List<AppInfo>, List<AppInfo>>() {
        @Override
        public List<AppInfo> apply(List<AppInfo> appInfos) throws Exception {
          int type = PreferenceManager.getDefaultSharedPreferences(context)
              .getInt("pref_app_sort_type", 0);
          Comparator<AppInfo> comparator = null;
          if (type == 0) {
            //按名称排序
          } else if (type == 2) {
            //按安装时间排序
            comparator = new Comparator<AppInfo>() {
              @Override
              public int compare(AppInfo o1, AppInfo o2) {
                return Long.compare(o2.installTime, o1.installTime);
              }
            };
          } else if (type == 3) {
            //按最后更新时间排序
            comparator = new Comparator<AppInfo>() {
              @Override
              public int compare(AppInfo o1, AppInfo o2) {
                return Long.compare(Math.max(o2.installTime, o2.updateTime),
                    Math.max(o1.installTime, o1.updateTime));
              }
            };
          }
  
          if (comparator != null) {
            Collections.sort(appInfos, comparator);
          }
  
          return appInfos;
        }
      };
    }
  
  
    public static List<OpEntryInfo> sortPermsFunction(Context context,
        List<OpEntryInfo> opEntryInfos) {
      //resort
      String groupS = null;
      PackageManager pm = context.getPackageManager();
  
      Map<String, List<OpEntryInfo>> sMap = new HashMap<String, List<OpEntryInfo>>();
  
      for (OpEntryInfo opEntryInfo : opEntryInfos) {
        if (opEntryInfo != null) {
  
          joinOpEntryInfo(opEntryInfo,context);
  
          List<OpEntryInfo> infos = sMap.get(opEntryInfo.groupName);
          if (infos == null) {
            infos = new ArrayList<OpEntryInfo>();
            sMap.put(opEntryInfo.groupName, infos);
          }
          infos.add(opEntryInfo);
  
        }
      }
  
      List<OpEntryInfo> infoList = new ArrayList<OpEntryInfo>();
      for (String string : RE_SORT_GROUPS) {
        List<OpEntryInfo> infos = sMap.get(string);
        if (infos != null) {
          infoList.addAll(infos);
        }
      }
  
      return infoList;
    }
  
    private static void joinOpEntryInfo(OpEntryInfo opEntryInfo,Context context){
      String groupS = FAKE_PERMS_GROUP.get(opEntryInfo.opName);
  
      try {
        if (groupS == null && opEntryInfo.opPermsName != null) {
          PermissionInfo permissionInfo = context.getPackageManager()
              .getPermissionInfo(opEntryInfo.opPermsName, PackageManager.GET_META_DATA);
          groupS = permissionInfo.group;
        }
      } catch (Exception e) {
        //ignore
      }
  
      PermGroupInfo permGroupInfo = null;
      if (groupS != null) {
        permGroupInfo = PERMS_GROUPS.get(groupS);
      }
  
      if (permGroupInfo == null) {
        permGroupInfo = OTHER_PERM_INFO;
      }
  
      opEntryInfo.icon = permGroupInfo.icon;
      opEntryInfo.groupName = permGroupInfo.group;
  
    }
  
    public static Single<List<OpEntryInfo>> groupByMode(final Context context,
        List<OpEntryInfo> list) {
  
      return Observable.fromIterable(list).collect(new Callable<List<OpEntryInfo>[]>() {
        @Override
        public List<OpEntryInfo>[] call() throws Exception {
          return new List[2];
        }
      }, new BiConsumer<List<OpEntryInfo>[], OpEntryInfo>() {
        @Override
        public void accept(List<OpEntryInfo>[] lists, OpEntryInfo opEntryInfo) throws Exception {
          if (opEntryInfo != null) {
            int idx = opEntryInfo.mode == AppOpsManager.MODE_ALLOWED ? 0 : 1;
            List<OpEntryInfo> list = lists[idx];
            if (list == null) {
              list = new ArrayList<OpEntryInfo>();
              lists[idx] = list;
            }
            list.add(opEntryInfo);
          }
        }
      }).map(new Function<List<OpEntryInfo>[], List<OpEntryInfo>>() {
        @Override
        public List<OpEntryInfo> apply(@NonNull List<OpEntryInfo>[] lists) throws Exception {
  
          List<OpEntryInfo> ret = new ArrayList<OpEntryInfo>();
          if (lists != null) {
            for (List<OpEntryInfo> list : lists) {
              if (list != null) {
                ret.addAll(Helper.sortPermsFunction(context, list));
              }
            }
          }
          return ret;
        }
      });
    }
  
  
    public static Single<List<UserInfo>> getUsers(final Context context,final boolean excludeDying){
      return Single.create(new SingleOnSubscribe<List<UserInfo>>() {
        @Override
        public void subscribe(SingleEmitter<List<UserInfo>> emitter) throws Exception {
          emitter.onSuccess(AppOpsx.getInstance(context).getApiSupporter().getUsers(excludeDying));
        }
      });
    }
  
  }
  