package com.yueban.installedpackage; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.support.annotation.IntDef; import android.support.design.widget.BaseTransientBottomBar; import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; import com.google.gson.Gson; import com.yueban.installedpackage.adapter.AppListAdapter; import com.yueban.installedpackage.entity.AppEntity; import com.yueban.installedpackage.rx.SimpleSubscriber; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.net.URISyntaxException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import rx.Observable; import rx.Subscriber; import rx.android.schedulers.AndroidSchedulers; import rx.functions.Func1; import rx.functions.Func2; import rx.schedulers.Schedulers; public class MainActivity extends AppCompatActivity { private static final int REQUEST_CODE_SELECT_FILE = 1; private RecyclerView mRecyclerView; private AppListAdapter mAdapter; @ViewType private int mType = ViewType.DEFAULT; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view); mRecyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this)); mAdapter = new AppListAdapter(); mRecyclerView.setAdapter(mAdapter); initData(null); } private void initData(String filePath) { final PackageManager packageManager = getPackageManager(); Observable<List<AppEntity>> applicationListObservable = Observable.from(packageManager.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES)) .filter(new Func1<ApplicationInfo, Boolean>() { @Override public Boolean call(ApplicationInfo info) { return (info.flags & ApplicationInfo.FLAG_SYSTEM) <= 0 || (info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 || (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0; } }) .map(new Func1<ApplicationInfo, AppEntity>() { @Override public AppEntity call(ApplicationInfo info) { return new AppEntity(info.loadLabel(packageManager).toString(), info.packageName, info.loadIcon(packageManager), true); } }) .toList(); switch (mType) { case ViewType.DEFAULT: applicationListObservable.subscribe(new SimpleSubscriber<List<AppEntity>>() { @Override public void onNext(List<AppEntity> entities) { mAdapter.setData(entities); } }); break; case ViewType.IMPORT: if (TextUtils.isEmpty(filePath)) { return; } Observable.just(filePath).map(new Func1<String, List<AppEntity>>() { @Override public List<AppEntity> call(String s) { return new Gson().fromJson(s, new ParameterizedType() { @Override public Type[] getActualTypeArguments() { return new Type[] { AppEntity.class }; } @Override public Type getOwnerType() { return null; } @Override public Type getRawType() { return List.class; } }); } }).zipWith(applicationListObservable, new Func2<List<AppEntity>, List<AppEntity>, List<AppEntity>>() { @Override public List<AppEntity> call(List<AppEntity> entities, List<AppEntity> entities2) { for (AppEntity entity : entities) { entity.isInstalled = !entities.contains(entity); } return entities; } }).subscribe(new SimpleSubscriber<List<AppEntity>>() { @Override public void onNext(List<AppEntity> entities) { mAdapter.setData(entities); } }); break; } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); MenuItem menuExport = menu.findItem(R.id.menu_export); menuExport.setVisible(mType == ViewType.DEFAULT); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_import: //importAppList(); Toast.makeText(this, "等待 Wi-Fi,或者设置关闭 “知识文件仅在 Wi-Fi 下上传/下载”", Toast.LENGTH_LONG).show(); break; case R.id.menu_export: exportAppList(); break; } return super.onOptionsItemSelected(item); } private void importAppList() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("*/*"); intent.addCategory(Intent.CATEGORY_OPENABLE); startActivityForResult(intent, REQUEST_CODE_SELECT_FILE); } @SuppressLint("SimpleDateFormat") private void exportAppList() { Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { String filePath = Environment.getExternalStorageDirectory() + File.separator + "_AppList_" + new SimpleDateFormat( "yyyy-MM-dd_HH:mm:ss").format(new Date()) + ".json"; try { String json = new Gson().toJson(mAdapter.getData()); FileWriter fileWriter = new FileWriter(filePath); fileWriter.write(json); fileWriter.flush(); if (!subscriber.isUnsubscribed()) { subscriber.onNext(filePath); subscriber.onCompleted(); } } catch (IOException e) { if (!subscriber.isUnsubscribed()) { subscriber.onError(e); } } } }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new SimpleSubscriber<String>() { @Override public void onNext(String s) { Snackbar.make(mRecyclerView, getString(R.string.app_list_save_to_format, s), BaseTransientBottomBar.LENGTH_LONG) .show(); } @Override public void onError(Throwable e) { Snackbar.make(mRecyclerView, R.string.export_failed, BaseTransientBottomBar.LENGTH_LONG).show(); e.printStackTrace(); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case REQUEST_CODE_SELECT_FILE: switch (resultCode) { case RESULT_CANCELED: Snackbar.make(mRecyclerView, R.string.cancel_select, BaseTransientBottomBar.LENGTH_LONG).show(); break; case RESULT_OK: Uri uri = data.getData(); String path; try { path = getPath(this, uri); switchToImportType(path); } catch (URISyntaxException e) { Snackbar.make(mRecyclerView, R.string.load_file_failed, BaseTransientBottomBar.LENGTH_LONG).show(); e.printStackTrace(); } break; } break; } } private void switchToImportType(String path) { mType = ViewType.IMPORT; invalidateOptionsMenu(); initData(path); } private String getPath(Context context, Uri uri) throws URISyntaxException { if ("content".equalsIgnoreCase(uri.getScheme())) { String[] projection = { "_data" }; Cursor cursor = null; try { cursor = context.getContentResolver().query(uri, projection, null, null, null); if (cursor == null) { throw new Exception("cursor init failed"); } int column_index = cursor.getColumnIndexOrThrow("_data"); if (cursor.moveToFirst()) { return cursor.getString(column_index); } } catch (Exception e) { // Eat it Or Log it. } finally { if (cursor != null) { cursor.close(); } } } else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } return null; } @Retention(RetentionPolicy.SOURCE) @IntDef({ ViewType.DEFAULT, ViewType.IMPORT }) public @interface ViewType { /** * 默认 */ int DEFAULT = 0; /** * 导入模式 */ int IMPORT = 1; } }