The whole answer relies on the fact that when this query is made (requires adb) :
adb shell pm list packages -3 -i
the installer for an app is listed in the output next to app's package name. We can filter the installer names in that output to single out apps not installed from Play Store.
Note: if you've a rooted Android OS, you may check /data/system/package.xml
since the said command exposes the information found in that file, but in a more legible manner.
Automation
I found Tasker (user guide) apt as well as flexible for the job. Create a task named NonMarket and create the following actions inside it:
Task → Run Shell
This will store the package name of all the third-party apps currently installed, but not from Play Store, in the variable AllPkgs. Replace -ve
with -e
in the said command if you intend to list apps currently installed and were installed from Play Store.
Any line in that variable would be following this template:
package:PACKAGE_NAME installer=INSTALLER_NAME
Here,
- PACKAGE_NAME → package name of the app;
- INSTALLER_NAME → package name of the installer app, such as com.android.vending or com.google.android.feedback for Play Store, org.fdroid.fdroid for F-Droid, null if installed manually by adb or from an ordinary file manager and so on.
Variables → Variable Search Replace
- Variable:
%AllPkgs
- Search:
package:
- tick Multi-Line
- tick Replace Matches
- Replace With: leave it untouched since we intend to remove the match from the lines
Variables → Variable Search Replace
- Variable:
%AllPkgs
- Search:
.*
Note that there is a whitespace before .*
and it has to be included.
- tick Multi-Line
- tick Replace Matches
- Replace With: leave it untouched since we intend to remove the match from the lines
After the execution of this step, each line in the variable AllPkgs would contain only the package name of an app.
Variables → Variable Set
- Name:
%ret
- To: tap Enter key in your keyboard for once
I learned this step from the post here by plepleus. This step is needed because we intend to copy each line (has a package name of an app) from AllPkgs into a separate variable. It is for ease of use.
Variables → Variable Split
- Name:
%AllPkgs
- Splitter:
%ret
This step makes an array of AllPkgs and each line of erstwhile AllPkgs is assigned to an index of that array. Hence, the size of the array is the number of lines AllPkgs had.
Variables → Variable Set
We're creating a variable named count and initializing it to 1, for the purpose of looping on all the elements of AllPkgs.
App → Test App
- Type: Package Name
- Data:
%All(%count)
- Store Result In:
%Label(%count)
This step finds an app's label from its package name and assign it to an element of array Label. See Variable Arrays in Tasker's userguide on Variables, if you're confused in this step.
Variables → Variable Set
- Name:
%AllLabels
- To:
%Label(%count) - %AllPkgs(%count)%ret
- tick Append
The last step, on its every execution in any cycle of the loop, would overwrite the value assigned to the variable mentioned in Store Result In. This step would assign that value into a new variable named AllLabels and Append makes sure that new cycle only adds but doesn't overwrite previous changes.
When the loop ends, AllLabels would be having lines where each of them would be having a particular app's label and package name.
Variables → Variable Add
This step increments Count variable, so that we can move to next cycle of loop.
Task → Goto
- Type: Action Number
- Number:
7
- If:
%count
<
%AllPkgs(#)+1
This step finally implements the loop. When this action is executed it checks whether count is less than the maximum number of elements in the array AllPkgs. If yes, it remains in the loop and creates a new cycle, else, the loop ends and we continue to rest of the actions below it.
File → Write File
- File:
/sdcard/unknown_sources.txt
- Text:
%AllLabels
This step writes all the lines stored in the variable AllLabels into a file named unknown_sources.txt under /sdcard
. If you're making replacement in the first action, make sure to change the file name in this action to keep the results stored separately.
Variables → Variable Clear → Name: %Label
You would end up with bizarre outcomes if you don't clear an array manually created by user and uses it in a task.
- Alert → Flash → Text:
Task completed
.
Run the task using the play button. Once the task ends check the file to find the content you're seeking.
Command-line
I couldn't ignore command-line which makes the objective so easy to be achieved in this particular case.
Dependencies
- Requires busybox tool. If you don't have it installed, refer to: How do I install missing command line tools onto an Android device?
Requires aapt tool. If you're running a CM or its derivative ROM then ignore this requirement. Otherwise, for Android 4.x, you can consider downloading the binary from here. For the purpose of installing it in device, follow the root or non-root approach mentioned here, appropriately.
Android 5.x users: ask Google for assistance.
My little script
#!/system/bin/sh
# Check if the busybox binary exists under /data/local/tmp/ or /system/xbin.
# Set the detected binary's path into the variable busybox or exit if file
# doesn't exist or executable permission is not set
if [[ -x /data/local/tmp/busybox ]]; then
busybox=/data/local/tmp/busybox;
elif [[ -x /system/xbin/busybox ]]; then
busybox=/system/xbin/busybox;
else
date +'busybox binary not found or executable permission is not set. Exiting';
exit;
fi
# Check if the aapt binary exists under /data/local/tmp or /system/bin or /system/xbin.
# Set the detected binary's path into the variable aapt or exit if file
# doesn't exist or executable permission is not set
if [[ -x /data/local/tmp/aapt ]]; then
aapt=/data/local/tmp/aapt;
elif [[ -x /system/bin/aapt ]]; then
aapt=/system/bin/aapt;
elif [[ -x /system/xbin/aapt ]]; then
aapt=/system/xbin/aapt;
else
date +'aapt binary not found or executable permission is not set. Exiting';
exit;
fi
# List third-party installed packages not installed from Play Store. Replace -ve with -e if you intend to list packages installed from Play Store.
pm list packages -3 -i | grep -ve "com.android.vending" -ve "com.google.android.feedback" | $busybox sed 's/package://g'| $busybox awk '{print$1}' > /sdcard/pkgs.txt
#$busybox printf "List of third-part installed apps not installed from Play Store:\n\n"
# For each package, get its label and print it with package name on standard output
while read line; do
path=$(pm path $line | $busybox sed 's/^package://g'); # retrieve path of package's APK. Required for finding app's label
label=$($aapt d badging $path 2>&1 | $busybox sed -ne '/application: label=/p' | $busybox cut -d "'" -f2); # Retrieve app's label using $path
printf "$label - $line\n";
done < /sdcard/pkgs.txt
Save the script under /sdcard
into a file named nonplay.sh
and run that file from a terminal emulator app in this manner:
sh /sdcard/nonplay.sh
Demo output:
(Click image to enlarge)
(Note: those apps listed in the image were downloaded from Play Store some time ago in my Primary ROM. Since they were sideloaded in this secondary ROM, they appeared in the output. If I successfully update any of the sideloaded app from Play Store, it will be considered that the app is installed from Play Store.)