├── .gitignore ├── .idea ├── caches │ └── build_file_checksums.ser ├── codeStyles │ └── Project.xml ├── dictionaries │ └── rajdeep1008.xml ├── gradle.xml ├── misc.xml └── runConfigurations.xml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── io │ │ └── github │ │ └── rajdeep1008 │ │ └── apkextractor │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── io │ │ │ └── github │ │ │ └── rajdeep1008 │ │ │ ├── adapters │ │ │ └── ApkListAdapter.kt │ │ │ ├── apkextractor │ │ │ └── MainActivity.kt │ │ │ ├── extras │ │ │ └── Utilities.kt │ │ │ └── models │ │ │ └── Apk.kt │ └── res │ │ ├── drawable-hdpi │ │ └── menu_btn.png │ │ ├── drawable-mdpi │ │ └── menu_btn.png │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable-xhdpi │ │ └── menu_btn.png │ │ ├── drawable-xxhdpi │ │ └── menu_btn.png │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ └── layout_apk_item.xml │ │ ├── menu │ │ └── context_menu.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── io │ └── github │ └── rajdeep1008 │ └── apkextractor │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singhsegv/ApkExtractor/777d529024e7505f5f7c6ad19e793a2200fa5acd/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/dictionaries/rajdeep1008.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 27 9 | defaultConfig { 10 | applicationId "io.github.rajdeep1008.apkextractor" 11 | minSdkVersion 19 12 | targetSdkVersion 27 13 | versionCode 1 14 | versionName "1.0" 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: 'libs', include: ['*.jar']) 27 | implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.51' 28 | implementation "org.jetbrains.anko:anko-common:0.10.5" 29 | implementation 'com.android.support:appcompat-v7:27.1.1' 30 | implementation 'commons-io:commons-io:2.4' 31 | implementation 'com.android.support:design:27.1.1' 32 | implementation 'com.android.support:cardview-v7:27.1.1' 33 | } 34 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/github/rajdeep1008/apkextractor/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.rajdeep1008.apkextractor 2 | 3 | import android.support.test.InstrumentationRegistry 4 | import android.support.test.runner.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getTargetContext() 22 | assertEquals("io.github.rajdeep1008.apkextractor", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/rajdeep1008/adapters/ApkListAdapter.kt: -------------------------------------------------------------------------------- 1 | package io.github.rajdeep1008.adapters 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.net.Uri 7 | import android.support.design.widget.Snackbar 8 | import android.support.v7.widget.RecyclerView 9 | import android.view.LayoutInflater 10 | import android.view.View 11 | import android.view.ViewGroup 12 | import android.widget.Button 13 | import android.widget.ImageButton 14 | import android.widget.ImageView 15 | import android.widget.TextView 16 | import io.github.rajdeep1008.apkextractor.MainActivity 17 | import io.github.rajdeep1008.apkextractor.R 18 | import io.github.rajdeep1008.extras.Utilities 19 | import io.github.rajdeep1008.models.Apk 20 | import org.jetbrains.anko.find 21 | import java.util.* 22 | 23 | /** 24 | * Created by rajdeep1008 on 20/04/18. 25 | */ 26 | 27 | class ApkListAdapter(var apkList: ArrayList, val context: Context) : RecyclerView.Adapter() { 28 | 29 | var mItemClickListener: OnContextItemClickListener? = null 30 | 31 | init { 32 | mItemClickListener = context as MainActivity 33 | } 34 | 35 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ApkListViewHolder { 36 | return ApkListViewHolder(LayoutInflater.from(context).inflate(R.layout.layout_apk_item, parent, false), context, apkList) 37 | } 38 | 39 | override fun onBindViewHolder(holder: ApkListViewHolder, position: Int) { 40 | holder.mIconImageView.setImageDrawable(context.packageManager.getApplicationIcon(apkList.get(position).appInfo)) 41 | holder.mLabelTextView.text = context.packageManager.getApplicationLabel(apkList.get(position).appInfo).toString() 42 | holder.mPackageTextView.text = apkList.get(position).packageName 43 | } 44 | 45 | override fun getItemCount(): Int { 46 | return apkList.size 47 | } 48 | 49 | inner class ApkListViewHolder(view: View, context: Context, apkList: ArrayList) : RecyclerView.ViewHolder(view) { 50 | 51 | val mIconImageView: ImageView = view.find(R.id.apk_icon_iv) 52 | val mLabelTextView: TextView = view.find(R.id.apk_label_tv) 53 | val mPackageTextView: TextView = view.find(R.id.apk_package_tv) 54 | private val mExtractBtn: Button = view.find(R.id.extract_btn) 55 | private val mShareBtn: Button = view.find(R.id.share_btn) 56 | private val mUninstallBtn: Button = view.find(R.id.uninstall_btn) 57 | private val mMenuBtn: ImageButton = view.find(R.id.menu_btn) 58 | 59 | init { 60 | 61 | (context as Activity).registerForContextMenu(mMenuBtn) 62 | 63 | mExtractBtn.setOnClickListener { 64 | if (Utilities.checkPermission(context as MainActivity)) { 65 | Utilities.extractApk(apkList.get(adapterPosition)) 66 | val rootView: View = context.window.decorView.findViewById(android.R.id.content) 67 | Snackbar.make(rootView, "${apkList.get(adapterPosition).appName} apk extracted successfully", Snackbar.LENGTH_LONG).show() 68 | } 69 | } 70 | 71 | mShareBtn.setOnClickListener { 72 | if (Utilities.checkPermission(context as MainActivity)) { 73 | val intent = Utilities.getShareableIntent(apkList.get(adapterPosition)) 74 | context.startActivity(Intent.createChooser(intent, "Share the apk using")) 75 | } 76 | } 77 | 78 | mUninstallBtn.setOnClickListener { 79 | val uninstallIntent = Intent(Intent.ACTION_UNINSTALL_PACKAGE) 80 | uninstallIntent.data = Uri.parse("package:" + apkList[adapterPosition].packageName); 81 | uninstallIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true) 82 | context.startActivity(uninstallIntent) 83 | } 84 | 85 | mMenuBtn.setOnClickListener { 86 | mItemClickListener?.onItemClicked(apkList.get(adapterPosition).packageName!!) 87 | context.openContextMenu(mMenuBtn) 88 | mMenuBtn.setOnCreateContextMenuListener { menu, _, _ -> 89 | context.menuInflater.inflate(R.menu.context_menu, menu) 90 | } 91 | } 92 | } 93 | } 94 | 95 | interface OnContextItemClickListener { 96 | fun onItemClicked(packageName: String) 97 | } 98 | } -------------------------------------------------------------------------------- /app/src/main/java/io/github/rajdeep1008/apkextractor/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package io.github.rajdeep1008.apkextractor 2 | 3 | import android.content.ActivityNotFoundException 4 | import android.content.Intent 5 | import android.content.pm.ApplicationInfo 6 | import android.content.pm.PackageInfo 7 | import android.content.pm.PackageManager 8 | import android.net.Uri 9 | import android.os.Bundle 10 | import android.support.design.widget.Snackbar 11 | import android.support.v7.app.AppCompatActivity 12 | import android.support.v7.widget.LinearLayoutManager 13 | import android.support.v7.widget.RecyclerView 14 | import android.view.MenuItem 15 | import android.view.View 16 | import android.widget.ProgressBar 17 | import io.github.rajdeep1008.adapters.ApkListAdapter 18 | import io.github.rajdeep1008.extras.Utilities 19 | import io.github.rajdeep1008.models.Apk 20 | import org.jetbrains.anko.doAsync 21 | import org.jetbrains.anko.find 22 | import org.jetbrains.anko.uiThread 23 | 24 | class MainActivity : AppCompatActivity(), ApkListAdapter.OnContextItemClickListener { 25 | 26 | private lateinit var progressBar: ProgressBar 27 | private val apkList = ArrayList() 28 | private lateinit var contextItemPackageName: String 29 | 30 | private lateinit var mAdapter: ApkListAdapter 31 | private lateinit var mLinearLayoutManager: LinearLayoutManager 32 | lateinit var mRecyclerView: RecyclerView 33 | 34 | override fun onCreate(savedInstanceState: Bundle?) { 35 | super.onCreate(savedInstanceState) 36 | setContentView(R.layout.activity_main) 37 | progressBar = find(R.id.progress) 38 | Utilities.checkPermission(this) 39 | 40 | mRecyclerView = find(R.id.apk_list_rv) 41 | mLinearLayoutManager = LinearLayoutManager(this) 42 | mAdapter = ApkListAdapter(apkList, this) 43 | 44 | mRecyclerView.layoutManager = mLinearLayoutManager 45 | mRecyclerView.adapter = mAdapter 46 | 47 | loadApk() 48 | } 49 | 50 | private fun loadApk() { 51 | doAsync { 52 | val allPackages: List = packageManager.getInstalledPackages(PackageManager.GET_META_DATA) 53 | 54 | allPackages.forEach { 55 | val applicationInfo: ApplicationInfo = it.applicationInfo 56 | 57 | val userApk = Apk( 58 | applicationInfo, 59 | packageManager.getApplicationLabel(applicationInfo).toString(), 60 | it.packageName, 61 | it.versionName) 62 | apkList.add(userApk) 63 | } 64 | 65 | uiThread { 66 | mAdapter.notifyDataSetChanged() 67 | progressBar.visibility = View.GONE 68 | } 69 | } 70 | } 71 | 72 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { 73 | when (requestCode) { 74 | Utilities.STORAGE_PERMISSION_CODE -> { 75 | if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { 76 | Utilities.makeAppDir() 77 | } else { 78 | Snackbar.make(find(android.R.id.content), "Permission required to extract apk", Snackbar.LENGTH_LONG).show() 79 | } 80 | } 81 | } 82 | } 83 | 84 | override fun onContextItemSelected(item: MenuItem?): Boolean { 85 | when (item?.itemId) { 86 | R.id.action_launch -> { 87 | try { 88 | startActivity(packageManager.getLaunchIntentForPackage(contextItemPackageName)) 89 | } catch (e: Exception) { 90 | Snackbar.make(find(android.R.id.content), "Can't open this app", Snackbar.LENGTH_SHORT).show() 91 | } 92 | } 93 | R.id.action_playstore -> { 94 | try { 95 | startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$contextItemPackageName"))) 96 | } catch (e: ActivityNotFoundException) { 97 | startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$contextItemPackageName"))) 98 | } 99 | 100 | } 101 | } 102 | return true 103 | } 104 | 105 | override fun onItemClicked(packageName: String) { 106 | contextItemPackageName = packageName 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/rajdeep1008/extras/Utilities.kt: -------------------------------------------------------------------------------- 1 | package io.github.rajdeep1008.extras 2 | 3 | import android.Manifest 4 | import android.content.Intent 5 | import android.content.pm.PackageManager 6 | import android.graphics.Color 7 | import android.net.Uri 8 | import android.os.Environment 9 | import android.support.design.widget.Snackbar 10 | import android.support.v4.app.ActivityCompat 11 | import android.support.v4.content.ContextCompat 12 | import android.support.v7.app.AppCompatActivity 13 | import android.util.Log 14 | import android.view.View 15 | import io.github.rajdeep1008.apkextractor.MainActivity 16 | import io.github.rajdeep1008.models.Apk 17 | import org.apache.commons.io.FileUtils 18 | import java.io.File 19 | 20 | /** 21 | * Created by rajdeep1008 on 22/04/18. 22 | */ 23 | class Utilities { 24 | 25 | companion object { 26 | 27 | val STORAGE_PERMISSION_CODE = 1008 28 | 29 | fun checkPermission(activity: AppCompatActivity): Boolean { 30 | var permissionGranted = false 31 | 32 | if (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { 33 | if (ActivityCompat.shouldShowRequestPermissionRationale(activity, 34 | Manifest.permission.WRITE_EXTERNAL_STORAGE)) { 35 | val rootView: View = (activity as MainActivity).window.decorView.findViewById(android.R.id.content) 36 | Snackbar.make(rootView, "Storage permission required", Snackbar.LENGTH_LONG) 37 | .setAction("Allow") { 38 | ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), STORAGE_PERMISSION_CODE) 39 | } 40 | .setActionTextColor(Color.WHITE) 41 | .show() 42 | } else { 43 | ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), STORAGE_PERMISSION_CODE) 44 | } 45 | } else { 46 | permissionGranted = true 47 | } 48 | 49 | return permissionGranted 50 | } 51 | 52 | fun checkExternalStorage(): Boolean { 53 | return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) 54 | } 55 | 56 | fun getAppFolder(): File? { 57 | var file: File? = null 58 | if (checkExternalStorage()) { 59 | file = File(Environment.getExternalStorageDirectory(), "ApkExtractor") 60 | return file 61 | } 62 | return file 63 | } 64 | 65 | fun makeAppDir() { 66 | val file = getAppFolder() 67 | if (file != null && !file.exists()) { 68 | file.mkdir() 69 | } 70 | } 71 | 72 | fun extractApk(apk: Apk): Boolean { 73 | makeAppDir() 74 | var extracted = true 75 | val originalFile = File(apk.appInfo.sourceDir) 76 | val extractedFile: File = getApkFile(apk) 77 | 78 | try { 79 | FileUtils.copyFile(originalFile, extractedFile) 80 | extracted = true 81 | } catch (e: Exception) { 82 | Log.d("test", "problem - " + e.message) 83 | } 84 | 85 | return extracted 86 | } 87 | 88 | fun getApkFile(apk: Apk): File { 89 | var fileName = getAppFolder()?.path + File.separator + apk.appName + "_" + apk.version + ".apk" 90 | return File(fileName) 91 | } 92 | 93 | fun getShareableIntent(apk: Apk): Intent { 94 | extractApk(apk) 95 | val file = getApkFile(apk) 96 | var shareIntent = Intent() 97 | shareIntent.setAction(Intent.ACTION_SEND) 98 | shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file)) 99 | shareIntent.setType("application/vnd.android.package-archive") 100 | shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 101 | 102 | return shareIntent 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /app/src/main/java/io/github/rajdeep1008/models/Apk.kt: -------------------------------------------------------------------------------- 1 | package io.github.rajdeep1008.models 2 | 3 | import android.content.pm.ApplicationInfo 4 | 5 | /** 6 | * Created by rajdeep1008 on 19/04/18. 7 | */ 8 | 9 | data class Apk(val appInfo: ApplicationInfo, 10 | val appName: String, 11 | val packageName: String? = "", 12 | val version: String? = "") -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/menu_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singhsegv/ApkExtractor/777d529024e7505f5f7c6ad19e793a2200fa5acd/app/src/main/res/drawable-hdpi/menu_btn.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/menu_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singhsegv/ApkExtractor/777d529024e7505f5f7c6ad19e793a2200fa5acd/app/src/main/res/drawable-mdpi/menu_btn.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/menu_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singhsegv/ApkExtractor/777d529024e7505f5f7c6ad19e793a2200fa5acd/app/src/main/res/drawable-xhdpi/menu_btn.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/menu_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singhsegv/ApkExtractor/777d529024e7505f5f7c6ad19e793a2200fa5acd/app/src/main/res/drawable-xxhdpi/menu_btn.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 12 | 13 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_apk_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 16 | 17 | 26 | 27 | 41 | 42 | 56 | 57 | 68 | 69 | 75 | 76 |