public class App extends Application {
    private static final String DB_NAME = "appplus.db";
    public static LiteOrm sDb;
    public static Context sContext;
    @Override
    public void onCreate() {
        super.onCreate();
        sDb = LiteOrm.newSingleInstance(this, DB_NAME);
        sContext = this;
        Once.initialise(this);
        Logger.init("AppPlusLog").setLogLevel(BuildConfig.IS_DEBUG?LogLevel.FULL:LogLevel.NONE);
        sDb.setDebugged(BuildConfig.IS_DEBUG);
    }
} @Table("app_entity") public class AppEntity  implements Parcelable{
    public static final String COLUMN_FAVORITE = "favorite";
    public static final String COLUMN_PACKAGE_NAME = "packageName";
    public static final String COLUMN_LAST_MODIFY_TIME = "lastModifyTime";
    public static final String COLUMN_TOTAL_SPACE = "totalSpace";
    @PrimaryKey(AssignType.AUTO_INCREMENT)
    @Column("_id") protected long id;

    @Column("appName") private String appName="";
    @Column(COLUMN_PACKAGE_NAME) private String packageName="";
    @Column("versionName") private String versionName="";
    @Column("versionCode") private int versionCode=0;
    @Column("appIconData") private byte[] appIconData=null;
    @Column("srcPath") private String srcPath;
    @Column("uid") private int uid;

    //add in 2016.6.21
    @Default("false")
    @Column(COLUMN_FAVORITE) private boolean isFavorite;
    //add in 2016.8.1
    @Column(COLUMN_LAST_MODIFY_TIME) private long lastModifyTime;
    @Column(COLUMN_TOTAL_SPACE) private long totalSpace;

    private int status;

    public AppEntity() {
    }

    public AppEntity(String packageName) {
        this.packageName = packageName;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getAppName() {
        return appName;
    }

    public void setAppName(String appName) {
        this.appName = appName;
    }

    public String getPackageName() {
        return packageName;
    }

    public void setPackageName(String packageName) {
        this.packageName = packageName;
    }

    public String getVersionName() {
        return versionName;
    }

    public void setVersionName(String versionName) {
        this.versionName = versionName;
    }

    public int getVersionCode() {
        return versionCode;
    }

    public void setVersionCode(int versionCode) {
        this.versionCode = versionCode;
    }

    public String getSrcPath() {
        return srcPath;
    }

    public void setSrcPath(String srcPath) {
        this.srcPath = srcPath;
    }

    public byte[] getAppIconData() {
        return appIconData;
    }

    public void setAppIconData(byte[] appIconData) {
        this.appIconData = appIconData;
    }

    public int getUid() {
        return uid;
    }

    public void setUid(int uid) {
        this.uid = uid;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public boolean isFavorite() {
        return isFavorite;
    }

    public void setFavorite(boolean favorite) {
        isFavorite = favorite;
    }

    public long getLastModifyTime() {
        return lastModifyTime;
    }

    public void setLastModifyTime(long lastModifyTime) {
        this.lastModifyTime = lastModifyTime;
    }

    public long getTotalSpace() {
        return totalSpace;
    }

    public void setTotalSpace(long totalSpace) {
        this.totalSpace = totalSpace;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        AppEntity entity = (AppEntity) o;

        if (versionCode != entity.versionCode) return false;
        if (appName != null ? !appName.equals(entity.appName) : entity.appName != null)
            return false;
        if (packageName != null ? !packageName.equals(entity.packageName) : entity.packageName != null)
            return false;
        if (versionName != null ? !versionName.equals(entity.versionName) : entity.versionName != null)
            return false;
        if (appIconData != null ? !(appIconData.length == entity.appIconData.length) : entity.appIconData != null)
            return false;
        return srcPath != null ? srcPath.equals(entity.srcPath) : entity.srcPath == null;

    }

    @Override
    public int hashCode() {
        return packageName != null ? packageName.hashCode() : 0;
    }



    @Override
    public String toString() {
        return "AppEntity{" +
                "appIconData size =" + appIconData.length +
                ", id=" + id +
                ", appName='" + appName + '\'' +
                ", packageName='" + packageName + '\'' +
                ", versionName='" + versionName + '\'' +
                ", versionCode=" + versionCode +
                ", srcPath='" + srcPath + '\'' +
                ", uid=" + uid +
                '}';
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeLong(this.id);
        dest.writeString(this.appName);
        dest.writeString(this.packageName);
        dest.writeString(this.versionName);
        dest.writeInt(this.versionCode);
        dest.writeByteArray(this.appIconData);
        dest.writeString(this.srcPath);
        dest.writeInt(this.uid);
        dest.writeByte(this.isFavorite ? (byte) 1 : (byte) 0);
        dest.writeLong(this.lastModifyTime);
        dest.writeLong(this.totalSpace);
        dest.writeInt(this.status);
    }

    protected AppEntity(Parcel in) {
        this.id = in.readLong();
        this.appName = in.readString();
        this.packageName = in.readString();
        this.versionName = in.readString();
        this.versionCode = in.readInt();
        this.appIconData = in.createByteArray();
        this.srcPath = in.readString();
        this.uid = in.readInt();
        this.isFavorite = in.readByte() != 0;
        this.lastModifyTime = in.readLong();
        this.totalSpace = in.readLong();
        this.status = in.readInt();
    }

    public static final Creator<AppEntity> CREATOR = new Creator<AppEntity>() {
        @Override
        public AppEntity createFromParcel(Parcel source) {
            return new AppEntity(source);
        }

        @Override
        public AppEntity[] newArray(int size) {
            return new AppEntity[size];
        }
    };
} public class AppInfoEngine {
    private Context mContext;
    private PackageManager mPackageManager;

    private static class SingletonHolder{
        private static final AppInfoEngine INSTANCE = new AppInfoEngine(App.sContext);
    }

    public static AppInfoEngine getInstance(){
        return SingletonHolder.INSTANCE;
    }

    private AppInfoEngine(Context context) {
        this.mContext = context;
        mPackageManager = mContext.getApplicationContext().getPackageManager();
    }

    /**
     * get all app info list which is installed by user
     * @return all installed app info list
     */
    public Observable<List<AppEntity>> getInstalledAppList() {
        return RxUtil.makeObservable(new Callable<List<AppEntity>>() {
            @Override
            public List<AppEntity> call() throws Exception {
                List<AppEntity> list = new ArrayList<>();
                List<PackageInfo>packageInfos = mPackageManager.getInstalledPackages(PackageManager.GET_META_DATA);
                for (PackageInfo info:packageInfos){
                    if(!isUserApp(info))continue;
                    AppEntity entity = warpAppEntity(info);
                    if (entity == null)continue;;
                    list.add(entity);
                }
                return list;
            }
        });
    }

    /**
     * get installed AppEntity by packageName
     * @param packageName Application's package name
     * @return installed app if not found package name will return null
     */
    public AppEntity getAppByPackageName(String packageName){
        List<PackageInfo>packageInfos = mPackageManager.getInstalledPackages(PackageManager.GET_META_DATA);
        for (PackageInfo packageInfo : packageInfos) {
            if(!isUserApp(packageInfo))continue;
            if(packageName.equals(packageInfo.packageName)){
                return warpAppEntity(packageInfo);
            }
        }
        return null;
    }

    /**
     * get recent running app list
     * @return recent running app list
     */
    @Deprecated
    public List<AppEntity> getRecentAppList() {
        List<AppEntity> list = new ArrayList<>();
        ActivityManager mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RecentTaskInfo> recentTasks = mActivityManager.getRecentTasks(10, 0);
        for (ActivityManager.RecentTaskInfo taskInfo : recentTasks) {
            Intent intent = taskInfo.baseIntent;
            ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, 0);
            if (resolveInfo == null)continue;
            String packageName = resolveInfo.activityInfo.packageName;
            if (isSystemApp(packageName)) continue;
            if (isShowSelf(packageName)) continue;
            AppEntity appEntity = DataHelper.getAppByPackageName(packageName);
            if (appEntity == null)continue;
            list.add (appEntity);
        }
        return list;
    }

    /**
     * check running list should show AppPlus or not
     * @param packagename
     * @return true if show else false
     */
    private boolean isShowSelf(String packagename){
        return !Utils.isShowSelf() && packagename.equals(mContext.getPackageName());
    }

    /**
     * check package is system app or not
     * @param packageName
     * @return if package is system app return true else return false
     */
    private boolean isSystemApp(String packageName){
        try {
            PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
            ApplicationInfo applicationInfo = packageInfo.applicationInfo;
            if(applicationInfo == null)return false;
            return ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            return false;
        }
    }

    boolean isUserApp(PackageInfo packageInfo) {
        if(packageInfo == null)return false;
        int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
        return (packageInfo.applicationInfo.flags & mask) == 0;
    }

    private boolean isSelf(String packageName) {
        return packageName.equals(mContext.getPackageName());
    }

    //////////////////////// Android L ///////////////////////////////////
    @Deprecated
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public List<UsageStats> getUsageStatsList(){
        UsageStatsManager usm = getUsageStatsManager();
        Calendar calendar = Calendar.getInstance();
        long endTime = calendar.getTimeInMillis();
        calendar.add(Calendar.MONTH, -1);
        long startTime = calendar.getTimeInMillis();
        List<UsageStats> usageStatsList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,startTime,endTime);
        return usageStatsList;
    }

    @Deprecated
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public List<AppEntity> getRecentAppInfo(){
        List<UsageStats> usageStatsList = getUsageStatsList();
        List<AppEntity> list = new ArrayList<>();
        for (UsageStats u : usageStatsList){
            String packageName = u.getPackageName();
            ApplicationInfo applicationInfo = getAppInfo(packageName);
            //system app will not appear recent list
            //if(isSystemApp(packageName))continue;
            if (isShowSelf(packageName)) continue;
            AppEntity entity = DataHelper.getAppByPackageName(packageName);
            if (entity == null)continue;
            list.add (entity);
        }
        return list;
    }

    @SuppressWarnings("ResourceType")
    @Deprecated
    private UsageStatsManager getUsageStatsManager(){
        UsageStatsManager usm = (UsageStatsManager) mContext.getSystemService("usagestats");
        return usm;
    }

    /**
     * make PackageInfo warp to AppEntity
     * @param packageInfo PackageInfo
     * @return AppEntity
     */
    private AppEntity warpAppEntity(PackageInfo packageInfo){
        if (packageInfo == null)return  null;
        AppEntity entity = new AppEntity();
        entity.setAppName(mPackageManager.getApplicationLabel(packageInfo.applicationInfo).toString());
        entity.setPackageName(packageInfo.packageName);
        Bitmap iconBitmap = drawableToBitmap(mPackageManager.getApplicationIcon(packageInfo.applicationInfo));
        entity.setAppIconData(formatBitmapToBytes(iconBitmap));
        entity.setSrcPath(packageInfo.applicationInfo.sourceDir);
        entity.setVersionName(packageInfo.versionName);
        entity.setVersionCode(packageInfo.versionCode);
        entity.setUid(packageInfo.applicationInfo.uid);
        return entity;
    }

    //根据包名获取对应的ApplicationInfo 信息
    private ApplicationInfo getAppInfo(String packageName){
        ApplicationInfo appInfo = null;
        try {
            appInfo = mPackageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            return null;
        }
        return appInfo;
    }

    /**
     * 将Drawable转化为Bitmap
     * @param drawable
     * @return
     */
    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 List<AppEntity> getRecentAppListV1() {
        List<AppEntity> list = new ArrayList<>();
        ActivityManager mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RecentTaskInfo> recentTasks = mActivityManager.getRecentTasks(10, 0);
        for (ActivityManager.RecentTaskInfo taskInfo : recentTasks) {
            Intent intent = taskInfo.baseIntent;
            ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, 0);
            if (resolveInfo == null)continue;

            if (isSystemApp(resolveInfo.resolvePackageName)) continue;

            ActivityInfo activityInfo = resolveInfo.activityInfo;
            if(activityInfo==null)continue;

            if (isShowSelf(activityInfo.packageName)) continue;
            AppEntity entity = new AppEntity();
            Bitmap bitmap = drawableToBitmap(resolveInfo.loadIcon(mPackageManager));
            entity.setAppIconData(formatBitmapToBytes(bitmap));
            entity.setAppName(resolveInfo.loadLabel(mPackageManager).toString());
            entity.setPackageName(activityInfo.packageName);
            ApplicationInfo applicationInfo = activityInfo.applicationInfo;
            if (applicationInfo == null)continue;

            if(applicationInfo.publicSourceDir!= null){
                entity.setSrcPath(applicationInfo.publicSourceDir);
            }
            list.add(entity);
        }
        return list;
    }

    private byte[] formatBitmapToBytes(Bitmap bitmap){
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG,100,bos);
        try {
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bos.toByteArray();
    }

    public void getRunningProcesses(){
        List<AndroidProcess>list = new ArrayList<>();
        File[]files = new File("/proc").listFiles();
        for (File file:files){
            if(file.isDirectory()){
                int pid;
                try{
                    pid = Integer.parseInt(file.getName());
                    AndroidProcess process = new AndroidProcess(pid);
                    Logger.i("pid is "+file.getName() +" name is "+process.name);
                    list.add(process);
                }catch (NumberFormatException e){
                    continue;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
} public enum AppStatus {
    /**
     * the AppEntity has been remove by some reason
     */
    REMOVE,
    /**
     * the AppEntity has changed
     */
    CHANGE,
    /**
     * this AppEntity is a new App
     */
    CREATE,
    /**
     * AppEntity not have any change
     */
    NORMAL;
} public class DataHelper {

   public static AppEntity checkAndSetAppEntityStatus(final AppEntity installedEntity){
        final AppEntity localResult = getAppByPackageName(installedEntity.getPackageName());
       //this app is a new app,now it not exist in my local db
       if(localResult ==null){
           installedEntity.setStatus(AppStatus.CREATE.ordinal());
           // the installed app info is change,so the
       } else if(!installedEntity.equals(localResult)){
           installedEntity.setStatus(AppStatus.CHANGE.ordinal());
       }else{
           installedEntity.setStatus(AppStatus.NORMAL.ordinal());
       }
       return installedEntity;
    }

    /**
     * query App info by local db
     * @param packageName Application's package name
     * @return return AppEntity if this package name is not exist db will return null
     */
    public static AppEntity getAppByPackageName(String packageName){
        if(TextUtils.isEmpty(packageName))return null;
        QueryBuilder queryBuilder = new QueryBuilder(AppEntity.class);
        queryBuilder = queryBuilder.whereEquals("packageName ", packageName);
        List<AppEntity>result = App.sDb.query(queryBuilder);
        try {
            return result.get(0);
        }catch (IndexOutOfBoundsException e){
            return null;
        }
    }

    public static Observable<List<AppEntity>>getAllEntityByDbAsyn(){
        return RxUtil.makeObservable(new Callable<List<AppEntity>>() {
            @Override
            public List<AppEntity> call() throws Exception {
                List<AppEntity> list = App.sDb.query(AppEntity.class);
                for (int i = 0; i < list.size(); i++) {
                    Logger.i(""+list.get(i).getAppName()+" is favorite "+list.get(i).isFavorite());
                }
                return list;
            }
        });
    }

    public static Observable<List<AppEntity>>getFavoriteEntityByDbAsyn(){
        return RxUtil.makeObservable(new Callable<List<AppEntity>>() {
            @Override
            public List<AppEntity> call() throws Exception {
                return App.sDb.query(new QueryBuilder<AppEntity>(AppEntity.class)
                        .whereEquals(AppEntity.COLUMN_FAVORITE, true));
            }
        });
    }

    public static AppEntity getEntityByPackageName(String packageName){
        List<AppEntity>list = App.sDb.query(
                new QueryBuilder<AppEntity>(AppEntity.class)
                .whereEquals(AppEntity.COLUMN_PACKAGE_NAME, packageName).limit(0,1));
        if(list.isEmpty()){
            return null;
        }
        return list.get(0);
    }

    /**
     * get AppEntity for Application of AppPlus
     * @return AppEntity
     */
    public static AppEntity getAppPlusEntity(){
        return getAppByPackageName(App.sContext.getPackageName());
    }

    /**
     * get the running app list info
     * @param ctx
     * @return
     */
    public static Observable<List<AppEntity>> getRunningAppEntity(final Context ctx) {
        return RxUtil.makeObservable(new Callable<List<AppEntity>>() {
            @Override
            public List<AppEntity> call() throws Exception {
                List<ActivityManager.RunningAppProcessInfo> runningList = ProcessManager.getRunningAppProcessInfo(ctx);
                Logger.i("=====","runing size is "+runningList.size());
                List<AppEntity> list = new ArrayList<>();
                for (ActivityManager.RunningAppProcessInfo processInfo : runningList) {
                    String packageName = processInfo.processName;
                    if (isNotShowSelf(ctx, packageName)) continue;
                    AppEntity entity = DataHelper.getAppByPackageName(packageName);
                    if (entity == null) continue;
                    list.add(entity);
                }
                return list;
            }
        });
    }

    @TargetApi(24)
    public static Observable<List<AppEntity>>getAppList(final Context ctx){
        return RxUtil.makeObservable(new Callable<List<AppEntity>>() {
            @Override
            public List<AppEntity> call() throws Exception {
                List<UsageStats> listStats = UStats.getUsageStatsList(ctx);
                List<AppEntity> list = new ArrayList<>();
                for (UsageStats stats:listStats) {
                    stats.getPackageName();
                    String packageName = stats.getPackageName();
                    if(packageName.contains("android") || packageName.contains("google")){
                        continue;
                    }
                    if (isNotShowSelf(ctx, packageName)) continue;
                    AppEntity entity = DataHelper.getAppByPackageName(packageName);
                    if (entity == null) continue;
                    list.add(entity);
                }
                return list;
            }
        });
    }

    public static Observable<List<AppEntity>>getExportedAppEntity(){
        return RxUtil.makeObservable(new Callable<List<AppEntity>>() {
            @Override
            public List<AppEntity> call() throws Exception {
                File parent = FileUtil.createDir(FileUtil.getSDPath(),FileUtil.KEY_EXPORT_DIR);
                File[]exportArray = parent.listFiles();
                List<AppEntity>exportList = new ArrayList<>();
                AppEntity entity = null;
                for(File file : exportArray){
                    ApkParser parser = ApkParser.create(file);
                    ApkMeta meta = parser.getApkMeta();
                    entity = new AppEntity();
                    entity.setAppName(meta.label);
                    entity.setAppIconData(parser.getIconFile().data);
                    entity.setVersionName(meta.versionName);
                    entity.setSrcPath(file.getAbsolutePath());
                    entity.setLastModifyTime(file.lastModified());
                    entity.setTotalSpace(file.length());
                    exportList.add(entity);
                }
                return exportList;
            }
        });
    }


    /**
     * check running list should show AppPlus or not
     * @param packagename
     * @return true if show else false
     */
    private static boolean isNotShowSelf(Context ctx, String packagename) {
        return !Utils.isShowSelf() && packagename.equals(ctx.getPackageName());
    }
} public enum EEvent {
    /**
     * in setting activity,when user clicked Show AppPlus setting item,this Event will be trigger
     */
    RECENT_LIST_IS_SHOW_SELF_CHANGE,
    /**
     * in setting activity,when user clicked Brief mode setting item,this Event will be trigger
     */
    LIST_ITEM_BRIEF_MODE_CHANGE,
    /**
     * uninstall a application from system,the app list need update data right now
     */
    UNINSTALL_APPLICATION_FROM_SYSTEM,
    /**
     * install a new application from system,the all app list need update date right now
     */
    INSTALL_APPLICATION_FROM_SYSTEM,
    /**
     * when enter SplashActivity app will load installed app and store list to local db
     * when load finish, need notify all installed list reload data,otherwise the list will empty
     */
    PREPARE_FOR_ALL_INSTALLED_APP_FINISH,
    /**
     * delete exported file successfully
     */
    DELETE_SINGLE_EXPORT_FILE_FAIL,
    /**
     * delete exported file fail
     */
    DELETE_SINGLE_EXPORT_FILE_SUC,

    UPDATE_ENTITY_FAVORITE_STATUS,

    /**
     * OPEN export dir
     */
    OPEN_EXPORT_DIR;
} public class RxBus<T, R>  {
    private final Subject<T,R> rxBus;

    private RxBus(){
        rxBus = new SerializedSubject(PublishSubject.<T>create());
    }

    private static class SingletonHolder{
        private static RxBus instance = new RxBus();
    }

    public static RxBus getInstance(){
        return SingletonHolder.instance;
    }

    public void send(T msg){
        rxBus.onNext(msg);
    }

    public Observable<R> toObservable(){
        return rxBus.asObservable().onBackpressureBuffer();
    }

    /**
     * check the observers has subscribe or not DeadEvent https://github.com/square/otto/blob/master/otto/src/main/java/com/squareup/otto/DeadEvent.java
     *
     * @return
     */
    public boolean hasObservers() {
        return rxBus.hasObservers();
    }
} public class RxEvent {
    private EEvent mType;
    private Bundle mData;

    public RxEvent() {
    }

    public RxEvent(EEvent type) {
        mType = type;
    }

    public RxEvent(EEvent type,Bundle data) {
        mData = data;
        mType = type;
    }

    public static RxEvent get(EEvent mType){
        return new RxEvent(mType);
    }

    public Bundle getData() {
        return mData;
    }

    public void setData(Bundle data) {
        mData = data;
    }

    public EEvent getType() {
        return mType;
    }

    public void setType(EEvent type) {
        mType = type;
    }
} public class NavigationManager {
    public static final int UNINSTALL_REQUEST_CODE = 1;

    private static final String SCHEME = "package";
    private static final String APP_PKG_NAME_21 = "com.android.settings.ApplicationPkgName";
    private static final String APP_PKG_NAME_22 = "pkg";
    private static final String APP_DETAILS_PACKAGE_NAME = "com.android.settings";
    private static final String APP_DETAILS_CLASS_NAME = "com.android.settings.InstalledAppDetails";


    /**
     * 给作者发送邮件 反馈意见
     *
     * @param context
     */
    public static void gotoSendOpinion(final Activity context) {

        Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
        emailIntent.setType("plain/text");
        emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"gudong.name@gmail.com"});
        emailIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.title_email_opinion));

        try {
            context.startActivity(emailIntent);
        } catch (ActivityNotFoundException e) {
            new AlertDialog.Builder(context)
                    .setTitle(R.string.title_point)
                    .setMessage("意见反馈需要你安装邮件客户端，检测到你的手机尚未使用任何邮件客户端，你可以立即去配置手机自带的邮件应用，也可以通过访问我个人主页的方式，跟我取得联系，再次感谢你对 AppPlus 支持！\n\n我的主页地址：gudong.name")
                    .setPositiveButton(R.string.dialog_know,null)
                    .setNegativeButton(R.string.dialog_cancel,null)
                    .setNeutralButton(context.getString(R.string.action_visit_host), new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            NavigationManager.openUrl(context,"http://gudong.name/");
                        }
                    })
                    .show();
        }
    }

    public static void openUrl(Activity activity,String url){
        Uri uri=Uri.parse(url);   //指定网址
        Intent intent=new Intent();
        intent.setAction(Intent.ACTION_VIEW);           //指定Action
        intent.setData(uri);                            //设置Uri
        activity.startActivity(intent);
    }

    /**
     * 去评分
     *
     * @param activity
     */
    public static void gotoMarket(Activity activity, String packageName) {
        Uri uri = Uri.parse("market://details?id=" + packageName);
        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        activity.startActivity(intent);
    }

    /**
     * 给朋友分享
     */
    public static void gotoShare(Activity activity) {
        Intent sendIntent = new Intent();
        sendIntent.setAction(Intent.ACTION_SEND);
        sendIntent.setType("text/*");
        sendIntent.putExtra(Intent.EXTRA_TEXT, "App+，一款不错的App管理应用");
        activity.startActivity(sendIntent);
    }

    public static void openApp(Context context, String packageName) throws Exception {
        Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName);
        context.startActivity(intent);
    }

    public static void uninstallApp(Activity activity, String packageName) {
        Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
        intent.setData(Uri.parse("package:" + packageName));
        intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
        activity.startActivityForResult(intent, UNINSTALL_REQUEST_CODE);
    }

    /**
     * 浏览文件夹
     *
     * @param file
     */
    public static void browseFile(Context context, File file) {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        Uri uri = Uri.fromFile(file);
        intent.setDataAndType(uri, "file/*");
        context.startActivity(intent);
    }

    /**
     * open app detail info
     *
     * @param packageName
     */
    public static void openAppDetail(Context context, String packageName) {
        Intent intent = new Intent();
        final int apiLevel = Build.VERSION.SDK_INT;
        if (apiLevel >= 9) {
            intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                    | Intent.FLAG_ACTIVITY_CLEAR_TOP);
            Uri uri = Uri.fromParts(SCHEME, packageName, null);
            intent.setData(uri);
        } else {
            final String appPkgName = (apiLevel == 8 ? APP_PKG_NAME_22
                    : APP_PKG_NAME_21);
            intent.setAction(Intent.ACTION_VIEW);
            intent.setClassName(APP_DETAILS_PACKAGE_NAME,
                    APP_DETAILS_CLASS_NAME);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.putExtra(appPkgName, packageName);
        }
        context.startActivity(intent);
    }

    public static void openAppInfo(Context context, String packageName) {
        Intent intent = new Intent(context,AppInfoActivity.class);
        intent.putExtra("package",packageName);
        context.startActivity(intent);

    }

    public static void gotoMainActivityFromSplashView(Activity context) {
        context.startActivity(new Intent(context, MainActivity.class));
        context.finish();
    }
} public class FileUtil {
    public static final String KEY_EXPORT_DIR_OLDER = "App+导出目录";
    public static final String KEY_EXPORT_DIR = "AppPlus";
    /**
     * 判断sd卡是不是处于挂载状态
     * @return
     */
    public static boolean isSdCardOnMounted(){
        String status = Environment.getExternalStorageState();
        if (status.equals(Environment.MEDIA_MOUNTED)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 获得sd卡根路径
     * @return
     */
    public static String getSDPath(){
        return Environment.getExternalStorageDirectory().toString();
    }

    /**
     * copy file
     * @param source
     * @param dest
     * @throws IOException
     */
    public static void copyFileUsingFileChannels(File source, File dest)
            throws IOException {
        FileChannel inputChannel = null;
        FileChannel outputChannel = null;
        try {
            inputChannel = new FileInputStream(source).getChannel();
            outputChannel = new FileOutputStream(dest).getChannel();
            outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
        } finally {
            inputChannel.close();
            outputChannel.close();
        }
    }

    /**
     * copy file
     * @param source
     * @param dest
     * @throws IOException
     */
    public static Observable<Boolean> copyFileUsingFileChannelsAsyn(final File source,final  File dest)
            throws IOException {
        return RxUtil.makeObservable(new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                FileChannel inputChannel = null;
                FileChannel outputChannel = null;
                try {
                    inputChannel = new FileInputStream(source).getChannel();
                    outputChannel = new FileOutputStream(dest).getChannel();
                    outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
                } finally {
                    inputChannel.close();
                    outputChannel.close();
                }
                return true;
            }
        });
    }

    /**
     * create a directory
     * @param parent parent directory
     * @param directoryName child directory name,it must a directory not file name
     */
    public static File createDir(String parent,String directoryName){
        File createdFile = new File(parent, directoryName);
        if (!createdFile.exists()) {
            createdFile.mkdir();
        }
        return createdFile;
    }

    public static boolean deleteExportedFile(AppEntity entity){
        if(entity == null)return false;
        File file = new File(entity.getSrcPath());
        return file.delete();
    }
} @TargetApi(24)
public class UStats {
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("M-d-yyyy HH:mm:ss");
    public static final String TAG = UStats.class.getSimpleName();
    @SuppressWarnings("ResourceType")
    public static void getStats(Context context){
        UsageStatsManager usm = (UsageStatsManager) context.getSystemService("usagestats");
        int interval = UsageStatsManager.INTERVAL_YEARLY;
        Calendar calendar = Calendar.getInstance();
        long endTime = calendar.getTimeInMillis();
        calendar.add(Calendar.YEAR, -1);
        long startTime = calendar.getTimeInMillis();

        Log.d(TAG, "Range start:" + dateFormat.format(startTime) );
        Log.d(TAG, "Range end:" + dateFormat.format(endTime));

        UsageEvents uEvents = usm.queryEvents(startTime,endTime);
        while (uEvents.hasNextEvent()){
            UsageEvents.Event e = new UsageEvents.Event();
            uEvents.getNextEvent(e);

            if (e != null){
                Log.d(TAG, "Event: " + e.getPackageName() + "\t" +  e.getTimeStamp());
            }
        }
    }

    public static List<UsageStats> getUsageStatsList(Context context){
        UsageStatsManager usm = getUsageStatsManager(context);
        Calendar calendar = Calendar.getInstance();
        long endTime = calendar.getTimeInMillis();

        long startTime = calendar.getTimeInMillis()-60*60*1000;
        
        List<UsageStats> usageStatsList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,startTime,endTime);
        return usageStatsList;
    }

    public static void printUsageStats(List<UsageStats> usageStatsList){
        for (UsageStats u : usageStatsList){
            Log.d(TAG, "Pkg: " + u.getPackageName() +  "\t" + "ForegroundTime: "
                    + u.getTotalTimeInForeground()) ;
        }

    }

    public static void printCurrentUsageStatus(Context context){
        printUsageStats(getUsageStatsList(context));
    }
    @SuppressWarnings("ResourceType")
    private static UsageStatsManager getUsageStatsManager(Context context){
        UsageStatsManager usm = (UsageStatsManager) context.getSystemService("usagestats");
        return usm;
    }
} public class Utils {
    private static final String KEY_IS_REFUSE_ANDROID_N_GET_RESENT_APP_LIST = "is_refuse_android_n_get_resent_app_list";
    private static final String KEY_DONT_SHOW_ANDROID_N_PERMISSION = "key_dont_show_android_n_get_resent_app_list";
    public static void setCurrentVersion(Context context, String version) {
        putStringPreference(context, "current_version", version);
    }

    public static String getLocalVersion(Context context) {
        return getStringPreference(context, "current_version", "");
    }

    public static void addAndroidNRefuseFlag(Context context) {
        putBooleanPreference(context, KEY_IS_REFUSE_ANDROID_N_GET_RESENT_APP_LIST, true);
    }

    public static boolean isResuleAndroidNPermission(Context context){
        return getBooleanPreference(context,KEY_IS_REFUSE_ANDROID_N_GET_RESENT_APP_LIST);
    }

    public static void addNotShowAndroidNRefuseFlag(Context context) {
        putBooleanPreference(context, KEY_DONT_SHOW_ANDROID_N_PERMISSION, true);
    }

    public static boolean dontShowAndroidNPermission(Context context){
        return getBooleanPreference(context,KEY_DONT_SHOW_ANDROID_N_PERMISSION);
    }

    /**
     * 获取主题强调色
     *
     * @param context
     * @return
     */
    public static int getAccentColor(Context context) {
        TypedValue typedValue = new TypedValue();
        Resources.Theme theme = context.getTheme();
        theme.resolveAttribute(R.attr.theme_accent_color, typedValue, true);
        return typedValue.data;
    }

    /**
     * 获取当前主题对应的暗色调
     *
     * @return
     */
    public static int getThemePrimaryDarkColor(Context context) {
        TypedValue typedValue = new TypedValue();
        Resources.Theme theme = context.getTheme();
        theme.resolveAttribute(R.attr.theme_color_dark, typedValue, true);
        return typedValue.data;
    }

    /**
     * 获取当前主题色对应色值
     *
     * @param context
     * @return
     */
    public static int getThemePrimaryColor(Context context) {
        TypedValue typedValue = new TypedValue();
        Resources.Theme theme = context.getTheme();
        theme.resolveAttribute(R.attr.theme_color, typedValue, true);
        return typedValue.data;
    }

    /**
     * 获取当前设置的主题
     *
     * @param context
     * @return
     */
    public static int getCurrentTheme(Context context) {
        int position = Utils.getIntPreference(context, "themePosition", 4);
        return ThemeControl.themeArr()[position];
    }

    /**
     * 获取color对应的int值
     *
     * @param context Activity
     * @param color   资源颜色id
     * @return 对应的int value
     */
    public static int getColorWarp(Activity context, @ColorRes int color) {
        return ContextCompat.getColor(context, color);
    }

    /**
     * running list is show AppPlus or not
     * @return return true if recent list view need show appplus
     */
    public static boolean isShowSelf() {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(App.sContext);
        return prefs.getBoolean(App.sContext.getString(R.string.switch_preference_key_show_self), false);
    }

    /**
     * list item is brief mode or not
     * @return return true if brief mode else not
     */
    public static boolean isBriefMode() {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(App.sContext);
        return prefs.getBoolean(App.sContext.getString(R.string.switch_preference_key_list_item_brief_mode), true);
    }

    public static boolean isAutoCheckWeChat() {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(App.sContext);
        return prefs.getBoolean(App.sContext.getString(R.string.preference_key_wechat_helper_auto_check), false);
    }


    /**
     * get app version info
     *
     * @param context context
     * @return app version info if occur exception return unknow
     */
    public static String getAppVersion(Context context) {
        try {
            PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            return info.versionName;
        } catch (Exception e) {
            e.printStackTrace();
            return context.getString(R.string.unknow);
        }
    }

    public static float getDensity(Context context) {
        return context.getResources().getDisplayMetrics().density;
    }

    public static int convertDensityPix(Context context, int dip) {
        float scale = getDensity(context);
        return (int) (dip * scale + 0.5f);
    }

    public static boolean isOwnApp(Activity activity, String packageName) {
        return activity.getPackageName().equals(packageName);
    }

    // ------------------- Language Info ------------------------------------------------

    /**
     * check current language is chinese or not
     *
     * @return true if it is else return false
     */
    public static boolean isChineseLanguage() {
        String language = getLanguageEnv();
        if (language != null && (language.trim().equals("zh-CN") || language.trim().equals("zh-TW")))
            return true;
        else
            return false;
    }


    private static String getLanguageEnv() {
        Locale l = Locale.getDefault();
        String language = l.getLanguage();
        String country = l.getCountry().toLowerCase();
        if ("zh".equals(language)) {
            if ("cn".equals(country)) {
                language = "zh-CN";
            } else if ("tw".equals(country)) {
                language = "zh-TW";
            }
        } else if ("pt".equals(language)) {
            if ("br".equals(country)) {
                language = "pt-BR";
            } else if ("pt".equals(country)) {
                language = "pt-PT";
            }
        }
        return language;
    }

    // -------------------    SharePreference Util Begin   -------------------  //

    public static void removeKey(Context context, String key) {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        sp.edit().remove(key).apply();
    }

    public static void putStringPreference(Context context, String key, String value) {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        sp.edit().putString(key, value).apply();
    }

    public static String getStringPreference(Context context, String key, String def) {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        return sp.getString(key, def);
    }

    public static void putIntPreference(Context context, String key, int value) {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        sp.edit().putInt(key, value).apply();
    }

    public static int getIntPreference(Context context, String key, int def) {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        return sp.getInt(key, def);
    }

    public static void putBooleanPreference(Context context, String key, boolean value) {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        sp.edit().putBoolean(key, value).apply();
    }

    public static boolean getBooleanPreference(Context context, String key) {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        return sp.getBoolean(key, false);
    }

    public static boolean isAndroidN(){
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
    }
} public class Logger {
    private static Setting setting;
    public static Setting init(String tag){
        if (tag == null) {
            throw new NullPointerException("tag may not be null");
        }
        if (tag.trim().length() == 0) {
            throw new IllegalStateException("tag may not be empty");
        }
        setting = Setting.getInstance();
        setting.setTag(tag);
        return setting;
    }


    public static void i(String message){
        log(Log.INFO, setting.getDefTag(), message);
    }

    public static void e(String message){
        log(Log.ERROR, setting.getDefTag(), message);
    }

    public static void i(String tag,String message){
        log(Log.INFO,tag, message);
    }

    public static void e(String tag,String message){
        log(Log.ERROR,tag, message);
    }

    public static void d(String tag,String message){
        log(Log.DEBUG,tag, message);
    }

    public static void w(String tag,String message){
        log(Log.WARN,tag, message);
    }

    private static synchronized void log(int logType, String tag,String msg) {
        if(setting == null){
            throw new NullPointerException("before use Logger ,please init Logger in Application and set param");
        }
        if (setting.getLevel() == LogLevel.NONE) {
            return;
        }
        String finalTag = formatTag(tag);
        switch (logType){
            case Log.INFO:
                Log.i(finalTag,msg);
                break;
            case Log.ERROR:
                Log.e(finalTag,msg);
                break;
            case Log.WARN:
                Log.w(finalTag,msg);
                break;
            case Log.DEBUG:
                Log.d(finalTag,msg);
                break;
        }
    }
    private static String formatTag(String tag) {
        if (!TextUtils.isEmpty(tag) && !TextUtils.equals(setting.getDefTag(), tag)) {
            return setting.getTag() + "-" + tag;
        }
        return setting.getTag();
    }
}
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v7.app.AlertDialog;
import com.gudong.appkit.App;
import com.gudong.appkit.R;
import com.gudong.appkit.dao.AppEntity;
import com.gudong.appkit.dao.AppInfoEngine;
import com.gudong.appkit.dao.AppStatus;
import com.gudong.appkit.dao.DataHelper;
import com.gudong.appkit.event.EEvent;
import com.gudong.appkit.event.RxBus;
import com.gudong.appkit.event.RxEvent;
import com.gudong.appkit.ui.control.NavigationManager;
import com.gudong.appkit.utils.FileUtil;
import com.gudong.appkit.utils.UStats;
import com.gudong.appkit.utils.Utils;
import com.gudong.appkit.utils.logger.Logger;
import com.umeng.analytics.MobclickAgent;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
/*
 *     Copyright (c) 2015 GuDong
 *
 *     Permission is hereby granted, free of charge, to any person obtaining a copy
 *     of this software and associated documentation files (the "Software"), to deal
 *     in the Software without restriction, including without limitation the rights
 *     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *     copies of the Software, and to permit persons to whom the Software is
 *     furnished to do so, subject to the following conditions:
 *
 *     The above copyright notice and this permission notice shall be included in all
 *     copies or substantial portions of the Software.
 *
 *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *     SOFTWARE.
 */

package com.gudong.appkit.ui.activity;





public class SplashActivity extends BaseActivity {
    private static final int GRUNT_RECENT_PREMISSION = 100;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // make view full screen
        //getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        super.onCreate(savedInstanceState);
        setStatusBarColorRes(R.color.colorPrimary);
        checkAndUpdateLocalDb();
        checkExportDirectoryIsChange();
        if(Utils.isAndroidN()){
            if (UStats.getUsageStatsList(this).isEmpty() && !Utils.dontShowAndroidNPermission(this)){
                new AlertDialog.Builder(this)
                        .setMessage("检测到您使用的是 Android N 设备，为了获取到最近运行的 App 列表，你需要授予 AppPlus 相关的权限，是否授予？")
                        .setPositiveButton(R.string.action_grunt, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
                                startActivityForResult(intent,GRUNT_RECENT_PREMISSION);
                                MobclickAgent.onEvent(SplashActivity.this, "android_N_and_grant_permission");
                            }
                        })
                        .setNegativeButton(R.string.action_refuse, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Utils.addAndroidNRefuseFlag(SplashActivity.this);
                                gotoMainActivity(false);
                            }
                        })
                        .setNeutralButton(R.string.dialog_do_not_point, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Utils.addNotShowAndroidNRefuseFlag(SplashActivity.this);
                                gotoMainActivity(false);
                            }
                        })
                        .setCancelable(false)
                        .show();
            }else{
                gotoMainActivity(true);
            }
        }else{
            gotoMainActivity(true);
        }

    }

    private void gotoMainActivity(boolean withDelayTime) {
        if(withDelayTime){
            //delay 1500 mill and enter MainActivity
            Observable.timer(500, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
                    .map(new Func1<Long, Object>() {
                        @Override
                        public Object call(Long aLong) {
                            NavigationManager.gotoMainActivityFromSplashView(SplashActivity.this);
                            return null;
                        }
                    })
                    .subscribe();
        }else{
            NavigationManager.gotoMainActivityFromSplashView(SplashActivity.this);
        }

    }

    /**
     * check local db data,because the installed App info will be changed or removed
     * so,we need check local data and update it before enter MainActivity
     */
    private void checkAndUpdateLocalDb(){
        AppInfoEngine.getInstance().getInstalledAppList()
                .subscribeOn(Schedulers.io())
                //将获取到的安装应用列表 appEntities 转换为单个的 AppEntity 对象
                .flatMap(new Func1<List<AppEntity>, Observable<AppEntity>>() {
                    @Override
                    public Observable<AppEntity> call(List<AppEntity> appEntities) {
                        List<AppEntity>listDB = App.sDb.query(AppEntity.class);