Layout inspector

App Cloner 1.5.25 comes with a new tool called Layout inspector, which allows visualizing the view hierarchy of cloned apps. You can access it from the pop-up menu under the Cloned apps tab.

Views are shown in a hierarchical tree, where you can tap the nodes to expand or collapse view groups (parent views that contain child views). Each node shows the view’s class name, e.g. LinearLayout, TextView, Button, etc. If a view has an identifier, it will be shown after the @ symbol. If a view has text (e.g. TextView, EditText or a text button), the text is shown in quotes. If a view is hidden or disabled you will see the Hidden or Disabled tags accordingly.

A DecorView represents the top of a view hierarchy. When an app is showing multiple windows or dialogs, you will have multiple top-level DecorView nodes. If the tree is completely empty, it means the app isn’t showing any activities or windows (you will probably need to launch the clone first).

The pop-up menu shown for each node allows the performing the following actions:

  • Highlight
    Marks the view in yellow, so it can be spotted easily inside the cloned app.
  • Show
    Shows the view. Hidden views only.
  • Hide
    Hides the view. Visible views only. Requires the Medium donation.
  • Enable
    Enables the view. Disabled views only.
  • Disable
    Disables the view. Enabled views only.
  • Check
    Allows selecting check-boxes, toggles or radio buttons. Checkable unchecked views only.
  • Uncheck
    Allows deselecting check-boxes, toggles or radio buttons. Checkable checked views only.
  • Click
    Sends a click event to the view (emulates a tap).
  • Focus
    Requests the view to receive focus.
  • Copy class
    Copies the view class to the clipboard. Useful for the Modify views option.
  • Copy identifier
    Copies the view identifier to the clipboard. Useful for the Modify views option.
  • Copy text
    Copies the view text to the clipboard. Text views only.
  • Edit text
    Allows editing the current view text. Text views only.

Note that any modified views may be quickly overwritten or reset by the cloned app. Especially when modifying views inside scrolling lists, the views are likely going to be replaced as soon as you scroll the list or when the app refreshes the list’s data. If you receive the message ‘Action was not performed’, the view hierarchy may no longer be valid. In this case tap the Refresh button in the toolbar to reload the view tree.

You may use the Search button to enter a search query. This will expand the tree and highlight any views whose class, identifier or view text contain the query text.

You can use the toolbar’s pop-up menu to expand or collapse all tree nodes or to clear any Highlight actions. In case the tree view contains deeply indented nodes, you can also scroll it horizontally.

Send broadcast on start

Send broadcast on start is a developer option, which sends an arbitrary broadcast when the cloned app is started. The broadcast is sent as soon as the app’s process is started, before any app activities may (or may not) be shown. This means the broadcast will also get sent when the cloned app starts any background services. The broadcast’s intent is empty and does not have any action nor intent extras set.

You need to specify the the broadcast receiver’s fully qualified component name in the form of <packageName>/<className>. Do not use the abbreviated form of <className>. When declaring the broadcast receiver in the manifest, make sure to mark it exported.

You may also use this option along with the Shared user ID and Process name options to trigger loading an external extension app into the same process as the cloned main app.

Shared user ID & Process name

App Cloner 1.5.22 adds two new developer options called Shared user ID and Process name.

The Shared user ID option allows generating clones with a specific shared user ID, an arbitrary string value, which must contain at least one ‘.’ separator. Apps with the same shared user ID can access each other’s data and, if desired, run in the same process, provided the apps are also signed with the same certificate.

The Process name option forces all app components to run in the named process. The process name must start with a lower case character and must also contain at least one ‘.’ separator.

This allows creating your own add-ons in separate apps, extending the clone’s functionality.

By signing the clone using a custom certificate (using the Custom certificate option) and creating your own app using the same shared user ID, process name and certificate, the clone and your app will run in the same process and virtual machine (VM).

Each app is executed using its own class loader, which loads classes from the corresponding APK .dex files, so a simple Class.forName("...") won’t work. To access the cloned app’s classes via reflection you must enumerate all threads, find the thread called ContextClassLoaderThread, which is provided by App Cloner, and use its context class loader to load the app’s classes.

Here’s an example in Kotlin:

val threads = arrayOfNulls<Thread>(Thread.activeCount())
Thread.enumerate(threads)
for (thread in threads) {
    if ("ContextClassLoaderThread" == thread!!.getName()) {
        val contextClassLoader = thread.getContextClassLoader()
        val clazz = contextClassLoader.loadClass("...")
        // TODO: Do something with the class
        break
    }
}

If the ContextClassLoaderThread thread cannot be found it means the clone hasn’t yet been launched into the process.

In order to get the cloned app’s Application instance you can use the Utils class provided by App Cloner:

val utilsClass = contextClassLoader.loadClass("com.applisto.appcloner.classes.Utils")
val getApplicationMethod = utilsClass.getMethod("getApplication")
val application: Application = getApplicationMethod.invoke(null) as Application

Once you have the Application instance you can register ActivityLifecycleCallbacks to get notified when the cloned app appears on the screen:

application.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
override fun onActivityStarted(activity: Activity) {}
override fun onActivityResumed(activity: Activity) {}
override fun onActivityPaused(activity: Activity?) {}
override fun onActivityDestroyed(activity: Activity?) {}
override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {}
override fun onActivityStopped(activity: Activity?) {}
})

The Shared user ID option is available with the medium donation, the Process name option is available with the large donation.

Incompatible options

As of App Cloner 1.5.27 the following options can be used on Android 10 but they are incompatible on all x86 systems like PC-based Android emulators (no matter which Android version). Also, they cannot be used on Android 4.x devices.

  • Hide root
  • Disable photo & media access
  • File access monitor
  • Flush Logcat buffer on exit

What to consider when cloning certain games

Some games store the current game state on a server in the cloud and use the device ID to look up and load this game data.

If you start the game without any data connection (no Wi-Fi and no mobile data) and the game refuses to launch, it’s likely that the game loads its data from the internet.

In this case, try the privacy options Change Android ID, Change IMEI/IMSI and Change Wi-Fi/Bluetooth MAC address to make each clone think it’s running on a different device.

Other games use a common folder on the SD-card to store the game state. Use a file manager app to check if the game created a folder under /sdcard/ (also called Internal Storage). You can also use the File access monitor option added in App Cloner 1.5.19 to see what directories an app creates or accesses.

In this case, you can use the storage option Redirect external storage. Here, external storage refers to the internal SD-card (as opposed in private internal app storage).

Floating app vs Free-form window vs Multi-window support

Here’s a short description on the differences between these similar options.

The option Multi-window support sets a flag to indicate the app is split-window capable. The Android system will then allow the app to be used in split window configuration. Normally this is limited to two apps visible at the same time.

The option Free-form window enables launching the app as a floating window but the implementation of these floating windows comes from the Android system and requires Android 7.0 or higher.

The option Floating app uses a custom floating window implementation that also works prior to Android 7.0 and may, in some cases, work better than free-form windows.

If you want to keep apps running and active in the background (e.g. prevent videos from being paused as the focus moves to another app), you should also consider enabling the Multi-window no pause option. This can be used in conjunction with any of the above options.