Re: PSA: How to archive your EXACT homescreen(s) apps to Windows for perfect cross-platform backup/restore sans the net
Maria Sophia <[email protected]>
| Newsgroups | comp.mobile.android,alt.comp.microsoft.windows,alt.comp.os.windows-10 |
|---|---|
| Organization | BWH Usenet Archive (https://usenet.blueworldhosting.com) |
| Message-ID | <[email protected]> |
Maria Sophia wrote:
> The goal is a perfect archive on sdcard or on the desktop of our
> Android homescreen(s) and all the folders & app APKs inside them.
This makes a skeleton on the desktop of the homescreen hierarchy.
But, there are two python scripts which need to be merged into one.
1. First, novasql.py creates a file containing panels, folders & apps
2. Then, novadir.py creates a physical folder hierarchy for each panel
# -------------------------------------------------------------------
# novasql.py
# Parse nova.db binary SQL database using "python novasql.py"
# python novasql.py > result.txt
# -------------------------------------------------------------------
# The "favorites" table stores app shortcuts and folder containers.
# -------------------------------------------------------------------
# v1p4 20260819 Ignore homescreen folders that don't have a valid title
# v1p3 20260819 Separates output by homescreen panels (0,1,2,etc.)
# v1p2 20260819 Cleaned up the output a bit to make it more readable
# v1p1 20260819 Try to map which app packages live inside each folder
# v1p0 20260819 Spits out the nova.db into a human-readable text file
# -------------------------------------------------------------------
# This creates an automated inventory of Android homescreen folders,
# loose desktop shortcuts and underlying Android application packages
# extracted from Nova Launcher internal databases (nova.db).
#
# Nova Launcher is a third-party customization interface for Android
# devices that replaces the default home screen. It organizes apps
# into custom desktop grids, user-defined folders and dedicated dock
# panels. This archive maps out that exact physical layout and group
# structure for long-term preservation and system migration reference.
#
# Last known good version of TeslaCoil Nova free/adfree launcher 7.0.57
# <https://mobile.softpedia.com/apk/nova-launcher/7.0.57/>
# -------------------------------------------------------------------
# Each folder on the homescreen gets a row in the favorites table
# with a unique ID and intent IS NULL. For any named folder,
# the title column stores that name. But if we never named the folder,
# then Nova stores it with a blank, null, or whitespace-only title.
# -------------------------------------------------------------------
import sys
import sqlite3
sys.stdout.reconfigure(encoding="utf-8")
conn = sqlite3.connect("nova.db")
cursor = conn.cursor()
print("=== NOVA LAUNCHER ARCHIVE: HOME SCREENS & FOLDERS ===\n")
# 1. Fetch distinct screen indices associated with desktop items
cursor.execute(
"SELECT DISTINCT screen FROM favorites WHERE container = -100 ORDER BY screen ASC;"
)
screens = cursor.fetchall()
for (screen_num,) in screens:
print(f"--- HOME SCREEN PANEL: {screen_num} ---")
# Find folders located directly on this specific screen,
# but ignore folders with blank titles
cursor.execute(
"SELECT _id, title FROM favorites WHERE container = -100 AND screen = ?"
" AND intent IS NULL AND title IS NOT NULL AND TRIM(title) != '';",
(screen_num,),
)
folders = cursor.fetchall()
for folder_id, folder_name in folders:
print(f" Folder: [ {folder_name} ] (ID: {folder_id})")
# Get apps inside this folder
cursor.execute(
"SELECT title, intent FROM favorites WHERE container = ?;",
(folder_id,),
)
items = cursor.fetchall()
if items:
for app_title, intent in items:
pkg = "Unknown"
if intent and "component=" in intent:
try:
part = intent.split("component=")[1]
pkg = part.split("/")[0]
except IndexError:
pkg = intent
print(f" - App: {app_title} ({pkg})")
else:
print(" - (Empty folder)")
# Find loose/top-level apps sitting directly on this screen (not in folders)
cursor.execute(
"SELECT title, intent FROM favorites WHERE container = -100 AND screen = ?"
" AND intent IS NOT NULL;",
(screen_num,),
)
loose_apps = cursor.fetchall()
if loose_apps:
print(" Loose Home Screen Apps:")
for app_title, intent in loose_apps:
pkg = "Unknown"
if intent and "component=" in intent:
try:
part = intent.split("component=")[1]
pkg = part.split("/")[0]
except IndexError:
pkg = intent
print(f" - {app_title} ({pkg})")
print()
conn.close()
# end of novasql.py
# -------------------------------------------------------------------
# novadir.py
# Create hierarchical-folder skeleton for Android homescreen APKs
# python novadir.py
# -------------------------------------------------------------------
# v1p3 20260819 Improved bad-data handling
# v1p2 20260819 Removed non-ASCII text
# v1p1 20260819 Solved unknown header bug
# v1p0 20260819 Creates a folder hierarchy based on nova.db output
# -------------------------------------------------------------------
# This creates a ./folder/app/ hierarchy for Android homescreen folders.
# -------------------------------------------------------------------
import os
import re
input_file = "screens_archive.txt"
base_dir = "./android_archive"
print(f"Reading archive from {input_file}...")
if not os.path.exists(input_file):
print(
f"Error: {input_file} not found. Please make sure it's in the same"
" directory."
)
exit(1)
current_panel = "panel_0"
current_folder = "loose_apps"
with open(input_file, "r", encoding="utf-8") as f:
for line in f:
line_stripped = line.replace("\xa0", " ").strip()
# Match Home Screen Panel headers accurately
panel_match = re.search(r"HOME\s+SCREEN\s+PANEL:\s*(-?\d+)", line_stripped)
if panel_match:
panel_id = panel_match.group(1)
current_panel = f"panel_{panel_id}"
current_folder = "loose_apps"
print(f"\n--- Switched to: {current_panel} ---")
continue
# Match Folder headers, e.g., "Folder: [ stuff ] (ID: 585)"
folder_match = re.search(r"Folder:\s*\[\s*(.*?)\s*\]", line_stripped)
if folder_match:
folder_name = folder_match.group(1).strip()
if not folder_name:
folder_name = "unnamed_folder"
current_folder = re.sub(r'[\\/*?:"<>|]', "", folder_name).strip()
continue
# Match App entries containing package names in parentheses at the end
app_match = re.search(r"\(([\w\.]+)\)\s*$", line_stripped)
if app_match and ("App:" in line_stripped or line_stripped.startswith("-")):
pkg_name = app_match.group(1)
target_path = os.path.join(
base_dir, current_panel, current_folder, pkg_name
)
os.makedirs(target_path, exist_ok=True)
print(f"Created: {target_path}")
print("\nSkeleton folder hierarchy successfully created!")
# end of novadir.py