https://swlock.blogspot.com/2018/01/apk-extractor.html
소스 설명이 없어서 준비하였습니다.
1. List는 기본
첫화면은 list로 구성되어 있습니다.일반적인 list는 앞에 아이콘을 배치하기 어렵기 때문에 Custom list를 사용하게 됩니다. list 구성에 대한 이해는 아래 링크를 참고 하시기 바랍니다.
https://swlock.blogspot.com/2017/03/custom-list-which-has-check-box-in.html
MainActivity의 onCreate 메소드부터 살펴보겠습니다.
크게 4개의 함수 호출을 하게됩니다.
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_applist); setupList(); setupAdapter(); setupFilter(); setupPermission(); }
setupList는 list에서 버튼을 눌렀을때 PackageSummary Activity를 띄워주기 위한 처리를 합니다.
private void setupList() { AdapterView.OnItemClickListener listener= new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { MyPackageInfo info = mAdapter.itemForPosition(position); if (info != null) { Intent intent = new Intent( null, Uri.fromParts("package", info.info.packageName, null)); intent.setClass(MainActivity.super.getBaseContext(), PackageSummary.class); startActivity(intent); } } }; listView=(ListView)findViewById(R.id.list); listView.setOnItemClickListener(listener); }
setupAdapter()는 AdapterAsyncTask class를 실행합니다.
코드는 간단하지만, 비동기적으로 AdapterAsyncTask 가 실행이 됩니다.
private void setupAdapter() { AdapterAsyncTask adaterAsyncTask = new AdapterAsyncTask(MainActivity.this); adaterAsyncTask.execute(); }
public class AdapterAsyncTask extends AsyncTask<String,Void,String> { private ProgressDialog mDlg; Context mContext; public AdapterAsyncTask(Context context) { mContext = context; } @Override protected void onPreExecute() { super.onPreExecute(); mDlg = new ProgressDialog(mContext); mDlg.setProgressStyle(ProgressDialog.STYLE_SPINNER); mDlg.setMessage( mContext.getString(R.string.loading)); mDlg.show(); } @Override protected String doInBackground(String... strings) { List<PackageInfo> pkgs = mContext.getPackageManager().getInstalledPackages(0); for (int i=0; i<pkgs.size(); i++) { MyPackageInfo info = new MyPackageInfo(); info.info = pkgs.get(i); info.label = info.info.applicationInfo.loadLabel(getPackageManager()).toString(); mPackageInfoList.add(info); } if (mPackageInfoList != null) { Collections.sort(mPackageInfoList, sDisplayNameComparator); } mdisplayPackageInfoList.addAll(mPackageInfoList); packageCount = mPackageInfoList.size(); return null; } @Override protected void onPostExecute(String s) { super.onPostExecute(s); mDlg.dismiss(); mAdapter = new PackageListAdapter(mContext); listView.setAdapter(mAdapter); } }
다음으로 setupFilter 인데요. 이건 list에서 검색을 할때 사용합니다. 돋보기 있는 칸에서 글씨를 쓰게되면 실시간으로 list에 필터를 걸어서 list의 adapter내용을 변경시킵니다. 그러면 list가 변경되게 됩니다.
private void setupFilter() { editView=(EditText)findViewById(R.id.editText_appfilter); editView.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { String searchText = editView.getText().toString(); if( mAdapter!= null ) mAdapter.fillter(searchText); } }); }
setupPermission는 안드로이드 23 부터는 내장 sdcard영역에 write권한을 획득하려면 runtime에 획득 하여야 합니다. 권한을 획득 하는 코드 입니다.
private void setupPermission() { if (Build.VERSION.SDK_INT >= 23) { if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE}, 1); } else { //do something } } else { //do something } }
2. Package 정보 얻어오기
패키지 list를 얻어오려면 기본적으로 휴대폰에 설치된 어플들의 목록을 얻어와야 합니다.여기에서는 AdapterAsyncTask class의 doInBackground 메소드 아래 코드에 의해서 이루어 집니다.
List<PackageInfo> pkgs = mContext.getPackageManager().getInstalledPackages(0);
3. PackageSummary Activity
PackageSummary는 구글 소스에서 가져왔습니다.Package의 자세한 정보들은 List에서 버튼을 눌렀을때 보여주도록 하였습니다. 이때 activity가 바뀌게 됩니다. 이때 패키지명을 intent로 전달하게 됩니다.
보낼때 Uri.fromParts로 보내면서 scheme을 "package"로 하였지만, 받을때는 scheme 부분이 중요하지는 않고 뒤쪽에 전달되는 packageName부분만 중요하기 때문에 getSchemeSpecificPart 메소드를 이용해서 얻어옵니다.Intent intent = new Intent( null, Uri.fromParts("package", info.info.packageName, null)); intent.setClass(MainActivity.super.getBaseContext(), PackageSummary.class); startActivity(intent);
이것을 getPackageInfo 메소드를 이용하면 좀더 많은 정보를 획득가능합니다.mPackageName = getIntent().getData().getSchemeSpecificPart();
info = pm.getPackageInfo(mPackageName, //PackageManager.GET_ACTIVITIES | //PackageManager.GET_RECEIVERS | //PackageManager.GET_SERVICES | //PackageManager.GET_PROVIDERS | PackageManager.GET_INSTRUMENTATION | PackageManager.GET_DISABLED_COMPONENTS | 0 );
4. APK 추출하기
Apk를 추출하려면 apk가 설치된 위치를 알아야합니다. 이것은 getPackageInfo 결과의 info.applicationInfo.sourceDir 값을 이용하면 알 수 있습니다.info = pm.getPackageInfo(mPackageName, //PackageManager.GET_ACTIVITIES | //PackageManager.GET_RECEIVERS | //PackageManager.GET_SERVICES | //PackageManager.GET_PROVIDERS | PackageManager.GET_INSTRUMENTATION | PackageManager.GET_DISABLED_COMPONENTS | 0 ); info.applicationInfo.sourceDir
해당 정보를 이용해서 미리 정해놓은 위치로 복사하면 됩니다.
copy(new File(info.applicationInfo.sourceDir), new File(sdcardPath+apkname+".apk"));
5. 전체 소스
소스는 이전에도 공유한 소스와 동일합니다.https://drive.google.com/file/d/1HPWLyD_SzdgsOmKcUBIXQ5eMnJ-Lcrot/view?usp=sharing
댓글 없음:
댓글 쓰기