I started to update our wear module to be up to date with latest guidelines of Google Play.
Since I'm doing so, I decided to add the Tiles API to the app. When ready for public release it will already be implemented.
Doc: https://developer.android.com/training/articles/wear-tiles#preview
Very unclear... Does anybody understand this?
The wear-tiles-renderer library provides a way to preview Tiles in an activity within your app.
To preview your Tile, create an activity that uses the renderer library to render the Tile. Add this activity in src/debug instead of src/main, as you’ll use this activity only for debugging purposes. See the following code sample for an example:
I tried adding the example code to the debug folder manually since I can't add it from Android Studio.
Added the xml file in the main and also added in debug folder to test.
When I load the app it opens my main file of folder src/main but freezes.
Do I need to add any code to load the example if in debug?
In Android Studio Dolphin you can now create a Launch configuration without the preview
What the quote from the documentation is trying to explain is that you need a module that looks something like this:
Your actual tile, and everything it needs, goes in the main folder. Then in the debug folder you need the following three things:
Manifest - Pretty standard
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.tiles">
<uses-feature android:name="android.hardware.type.watch" />
<application
android:label="#string/tiles_app_name">
<activity
android:name=".MainActivity"
android:label="#string/tiles_app_name"
android:noHistory="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Activity - The important thing here is the TileManager
class MainActivity : ComponentActivity() {
private lateinit var tileManager: TileManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.tile_test)
val rootLayout = findViewById<FrameLayout>(R.id.tile_container)
tileManager = TileManager(
context = this,
component = ComponentName(this, SampleTile::class.java),
parentView = rootLayout
)
tileManager.create()
}
override fun onDestroy() {
super.onDestroy()
tileManager.close()
}
}
Layout - Also pretty standard
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/tile_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
If you have all of this set up and it's still freezing, you probably have a bug in your Tile class. See if there are any error messages in Logcat that can help you understand what is going wrong.
If you split your TileService and Tile layout code using Horologist
You can use Android Studio previews for this
#WearSmallRoundDevicePreview
#WearLargeRoundDevicePreview
#Composable
fun Run() {
val context = LocalContext.current
LayoutRootPreview(
Run.layout(
context,
context.deviceParams(),
lastRunText = "2 days ago",
startRunClickable = emptyClickable,
moreChipClickable = emptyClickable
)
)
}
See the example here https://github.com/android/wear-os-samples/blob/main/WearTilesKotlin/app/src/debug/java/com/example/wear/tiles/golden/GoldenTilesPreviewsRow1.kt
Related
I want to create an android app which will "scan" other applications'layouts and find out if there are buttons etc.
Some apps, like whatsapp, doesn't allow third parts to move into the app, so I though that using accessibility could be the solution.
Now the problem is that I've never used Accessibility, so can someone of you can please show me how to "scan" an app's layout to find buttons?
Thanks a lot
What you're looking for is an Accessibility Service. Configuring an accessibility service is somewhat complicated. I have set up a repository of accessibility boilerplate code that sets up an AccessibilityService that logs the node heirarchy to LogCat, and has the default project settings activity set up as it's settings activity. Here are some of the highlights, as I dislike just posting github repos as answers. Note that I use a lot of my own libraries. CLog is a logging library and AndroidAccessibilityUtils wraps node infos with some common utility functions. You can find references to the dependencies in the build.gradle file on the github repo at the bottom. Here are some code highlights.
Your manifest.xml file is going to be significantly different from an Activity, and should contain an entry like this in your Application element:
<application .... >
...
<service
android:name="com.moba11y.basicaccessibilityservice.BasicAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" >
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="#xml/service_config" />
</service>
...
</application>
First you need to create a subclass of Accessibility Service:
public class BasicAccessibilityService extends AccessibilityService {
static {
CLog.initialize(BasicAccessibilityService.class.getSimpleName(), BuildConfig.DEBUG);
}
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
CLog.d(event.toString());
switch (event.getEventType()) {
//On Gesture events print out the entire view heirarchy!
case AccessibilityEvent.TYPE_GESTURE_DETECTION_START:
CLog.d(A11yNodeInfo.wrap(getRootInActiveWindow()).toViewHeirarchy());
default: {
//If the event has a source, let's print it out separately.
if (event.getSource() != null) {
CLog.d(A11yNodeInfo.wrap(event.getSource()).toViewHeirarchy());
}
}
}
}
#Override
public void onInterrupt() {
CLog.e("Service Interrupted: Have never actually had this happen.");
}
}
That is the bulk of the highlights. You also should have a "service_config" XML with properties, as is referenced in the changes to the Manifest XML file. More details, and a reasonable starer point can be found in the open source repo on GitHub.
https://github.com/chriscm2006/Android-Accessibility-Service-Boilerplate
I am new to Espresso UI testing.
I am getting this error while running tests (ADT Eclipse IDE ).
The app is already developed and there are lots of request going on while launching the app. it is not possible to rewrite the app. but i need to find the way to test this UI even if there is any delay in the loading of the components.
java.lang.RuntimeException: Could not launch intent Intent { act=android.intent.action.MAIN flg=0x14000000 cmp=com.xx.android/com.yy.core.android.map.MapActivity } within 45 seconds. Perhaps the main thread has not gone idle within a reasonable amount of time? There could be an animation or something constantly repainting the screen. Or the activity is doing network calls on creation? See the threaddump logs. For your reference the last time the event queue was idle before your activity launch request was 1390913271702 and and now the last time the queue went idle was: 1390913271767. If these numbers are the same your activity might be hogging the event queue.
at com.google.android.apps.common.testing.testrunner.GoogleInstrumentation.startActivitySync(GoogleInstrumentation.java:277)
at android.test.InstrumentationTestCase.launchActivityWithIntent(InstrumentationTestCase.java:119)
at android.test.InstrumentationTestCase.launchActivity(InstrumentationTestCase.java:97)
at android.test.ActivityInstrumentationTestCase2.getActivity(ActivityInstrumentationTestCase2.java:104)
at com.gulesider.android.test.UItest.setUp(UItest.java:25)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:190)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:175)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:555)
at com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner.onStart(GoogleInstrumentationTestRunner.java:167)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1799)
I have one library project called “Core” - it will not generate any .apk
Also i have one Android project called “AA” which will access “Core”. - This is AA.apk
Now i have created a test project called “UItest”
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.AA.android.test"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="8"
android:targetSdkVersion="18" />
<instrumentation
android:name="com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner"
android:targetPackage="com.AA.android"/>
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
<activity
android:name="com.core.android.map.MapActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<uses-library android:name="android.test.runner" />
</application>
</manifest>
My test:
public class UItest extends ActivityInstrumentationTestCase2<MapActivity> {
public UItest() {
super(MapActivity.class);
}
#Override
public void setUp() throws Exception {
super.setUp();
getActivity();
}
public void testSearchBox() {
Espresso.onView(ViewMatchers.withId(R.id.menu_button_logo)).perform(ViewActions.click());
}
}
For Espresso Testing it is highly recommend that you turn off system animations on the virtual or physical device(s) used for testing. So you can follow the steps below to manually turn off the animations:
Under:
Settings->
Developer options->
Drawing
Window Animations scale to OFF
Transition animation scale to OFF
Animator duration scale to OFF
If there is a progress bar running when you create the activity, you get an error like this. You should cause a stop for the progress bar in order to continue running the test.
I experienced this error while running Espresso tests on 6.0 devices but not on 5.1.1 or 7.0 devices. I tracked the cause down to using android:fadeScrollbars within a style. Removing this item from my style resolved the issue.
I have stuck into this problem for several hours. Finally, I got the reason.
This works for me.
Here are some different reasons, according to the phenomenon.
Activity can't be launched
Activity launched, but UI perform actions not work
The first scenario: activity can't be launched
Because of your target Activity maybe already in the activity stack.
Add a CLEAR flag and NEW_TASK flag
#get:Rule
val activityRule = ActivityTestRule<MainActivity>(MainActivity::class.java)
private lateinit var launchedActivity: MainActivity
#Before
fun setUp() {
val intent = Intent(Intent.ACTION_PICK)
//this is the key part
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
//this is the key part
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
launchedActivity = activityRule.launchActivity(intent)
}
The second scenario: activity launched but UI perform actions not work
In this scenario may be because of
Your code is executing some animation in a long time
Your code is doing a UI logic that you don't notice
(like Handler post event or runnable)
(register a listener in ViewTreeObserver like OnPreDrawListener and didn't unregister in a proper timing)
These actions may lead to UI thread busy, so the espresso can not do the test.
Probably you have animation inside your activity, which blocks espresso execution. You have to disable it - see https://github.com/googlesamples/android-testing/tree/master/ui/espresso/BasicSample
In my case a custom view caused this behaviour. It contained a Scroller which was constantly scrolling. Unfortunately, I didn't find a solution for this issue until now except disabling it for the tests...
If you are performing this test in MI or XIOMI phone then maybe it will not work so you can change device or you can use bluestack emulator. It will be work
At the very first page you will be calling too many request which will be taking time more than 15 seconds, the first page should be very lightwieght.
Just try by creating one new welcome page and then calling your original welcome page.
Hope this work for you.
Well, in my case it was caused by a strange thing.
One of my UI tests opened the external intent "android.app.action.CONFIRM_DEVICE_CREDENTIAL" so I decided to stub it. From now on, the MAIN intent didnt launch again until I manually closed the screen oipened by "android.app.action.CONFIRM_DEVICE_CREDENTIAL" from the recent tasks.
No idea why this happened, and have no time now for research. Maybe later I will update this thread.
I faced this error when I trying to test the opening of another activity when the user clicked on a given view. What I was doing wrong was not replacing:
#Rule
public ActivityTestRule<MyActivity> myActivityActivityTestRule = new ActivityTestRule<>(MyActivity.class);
Per:
#Rule
public IntentsTestRule<MyActivity> myActivityActivityTestRule =
new IntentsTestRule<>(MyActivity.class);
I had this problem too, and in the moment I changed physical device to other Android phone the tests were working. Just try to use other device. And use #rule for launching activity
I've been having this problem for almost 2 months now and can't figure it out. The problem is that if my application is running and I run (reinstall) my application from Eclipse, I get an error message indicating that my application has crashed 'Unfortunately, has stopped.'. I notice that it also occurs when I run it away from my PC/Eclipse, I think that it happens only after I don't run it for a while.
It only occurs if the app is active in the 3rd activity (BaseDiagramActivity) and then I run the app again from Eclipse. I've stripped out basically all the application except the 3 activities and It's still happening.
I've searched and searched for a solution to this problem but can't find any good answer or one that applies to me.
It doesn't seem like a hardware or android version issue as I'm running this on my tablet (4.0.3) and my phone (4.0.2, was happening on 4.0.1 before update). Unless of course it is an ice cream sandwich bug.
Let me know if any more info is required.
The exception (Tag=AndroidRuntime)
FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to instantiate application android.app.Application: java.lang.NullPointerException
at android.app.LoadedApk.makeApplication(LoadedApk.java:482)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:3938)
at android.app.ActivityThread.access$1300(ActivityThread.java:123)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1185)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4424)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at android.app.LoadedApk.initializeJavaContextClassLoader(LoadedApk.java:362)
at android.app.LoadedApk.getClassLoader(LoadedApk.java:305)
at android.app.LoadedApk.makeApplication(LoadedApk.java:474)
... 11 more
The Android Code
LoadedApk.initializeJavaContextClassLoader() - Line 362 seems to be the offender
Below are the relevant files:
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="[my package]"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="14" />
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
<activity
android:name="HomeActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="LoadDiagramActivity" android:label="Load Diagram"></activity>
<activity android:name="BaseDiagramActivity" android:label="Base Diagram"></activity>
</application>
</manifest>
HomeActivity.java
public class HomeActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home);
Button diagramButton = (Button)findViewById(R.id.diagram);
diagramButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
startActivity(new Intent(HomeActivity.this, LoadDiagramActivity.class));
}
});
}
}
LoadDiagramActivity.java
public class LoadDiagramActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.load_diagram_menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
case R.id.add_new_diagram:
startActivity(new Intent(this, BaseDiagramActivity.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
BaseDiagramActivity.java
it doesn't actually matter what activity this is, the exception occurs as long as a 'third' activity is started (or clicking the add button on LoadDiagramActivity.
public class BaseDiagramActivity extends Activity {
}
home.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="#+id/diagram"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Diagram" />
</LinearLayout>
Additional information
When I stripped down my project in order to ask a simpler answer, I moved everything into the package's namespace. In the actual project there are 5 namespaces, they were still present when I was testing with the stripped down version however just not called (as far as I could see).
Here are the packages:
[package] - general logic
[package].activities - all activities and base activities
[package].database - all interaction with the database
[package].models - models for saving/loading data
[package].renderables - objects drawn to a canvas
I have tried to add an `android:sharedUserId' attribute to the manifest and and it seemed to do nothing both times I tried. When I was initially investigating this I came to the conclusion that the shared user id only applied to different projects, not different packages.
Also I don't believe there was any interaction with the database when I stripped everything down. The fact that the 3rd activity could be any activity, even HomeActivity, was something against this theory.
Useful links
stackoverflow: android.app.Application cannot be instantiated due to NullPointerException
GreoCode android.app.LoadedApk on 4.0.1
Possible race condition?
Android issue #25869
Update 1/11/2012
Last couple of days I've jumped back into this project, I created a brand new project in Eclipse Juno (was on Helios before) and transferred everything over manually so that Eclipse and Android tools handled almost all of the Manifest interaction but it's still occurring. I will look at it a bit more over the next few days and update if I find anything.
FYI my new project is targeting the following:
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="15" />
The new project structure also has all the activities in the root package now (ie. [package], not [package].activities). I'm also using the (new?) syntax to show the parent activity:
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="[my package].LoadDiagramActivity" />
It is also still occurring on my now updated Galaxy Nexus running Jellybean 4.1.2.
Try adding one more thing in Manifest file, I am sure you have already tried..
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="[my package]"
android:versionCode="1"
android:versionName="1.0"
android:sharedUserId="com.mj.app" >
It seems like in your project you have multiple PACKAGES, like com.package.p1 , com.package.p2 ,com.package.p3..
And while starting you are in p1 and then moves on to p2 - p3...
and at that time you try to run it again..so the Android gives you error.
If you put 13 in your minSdkVersion, it should work.
Either that, or you need to setDisplayHomeAsUpEnabled(false) in the onCreate() of your other activities.
Both those solutions should work.
Starting at API Level 14 according to the documentation, the setHomeButtonEnabled(true) is no longer done for you by default, and it goes on to say that
Setting the DISPLAY_HOME_AS_UP display option will automatically enable the home button.
So we can infer that setDisplayHomeAsUpEnabled(false) works in a similar way as setHomeButtonEnabled(false)
If I am not mistaken, this only happens when we reinstall the app from Eclipse Run->As. So this is unlikely to happen when a user upgrades through Play. I can say this with confidence since I did notice my app too with this exception during reinstall through Eclipse, but nothing on Crittercism.
To fix this, I worked on resolving memory consumption of my app.
If you only have 1 set of drawables, then change that. Create a drawables-mdpi and copy all the file from that 1 drawables folder (drawables, drawable-ldpi). If you have just 1 set, Android will resize it for its use on bigger screens, thus internally taking up too much memory, and cleanup of this kind of memory (bitmap) is bug prone. Sound crazy, but does wonders. Dont believe me, run it under Heap memory usage watch before and after this change, you will notice that your app is taking 25% less memory for a common scenario.
If you are doing any Bitmap operations, you may want to consider downscaling. Atleast when you are setting options you definitely want to downscale it. Check here and here. Search or downscaleing.
Finally dont forget to bitmap.recycle(); bitmap = null; in your onDestroy and before all System.exit(0).
Android does a lot of background work, and reinstallation is a abrupt cleanup expectation from the app. Memory not cleaned up can cause issues. This exception is one of those internal ones. So dont think of it to be straightforward.
You haven't added the period to the names of your activities in manifest's android:name attributes. Perhaps, this causes the problem.
Wait after your application crashes when u run it second time. The app will launch after the crashing again. This happens sometimes with me. If the same is the case with u dont forget to clean your project everytime before u run it.
By inspecting your code I feel you are skipping to set "setContentView(rid)" in LoadDiagramActivity. Please try setting the view in onCreate().
Hope this will help you.
You need to append "." before the activity name in Menifest.
Like this
<?xml version="1.0" encoding="utf-8"?>
<uses-sdk android:minSdkVersion="14" />
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
<activity
android:name=".HomeActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".LoadDiagramActivity" android:label="Load Diagram"></activity>
<activity android:name=".BaseDiagramActivity" android:label="Base Diagram"></activity>
</application>
I update your menifest's activity. Please check it...
There is nothing wrong with the code you've given us so far. I just tried it in a new project with the package name com.test and it worked flawlessly. The only thing I added in the files were the package declaration. (And I added the load_diagram_menu.xml, of course.)
If possible, it would be great if you could give us access to the complete project. If the project is working on someone else's computer, it's most likely Eclipse or the Android Eclipse plugin-in that are misbehaving. A clean install of Eclipse would solve that. (Though I understand how being offline could make this difficult. :-) )
For this, be sure to have the "Build Automatically" checked when cleaning the project. (Project -> Build Automatically)
I have an application using the Google maps - until the moment it works fine. But now when I want to click a button in order to add functionality over the map I have problems.
I managed to visualise the button on the screen, also it works on click - it shows a toast correctly. But my aim is to start a new activity (having his own layout) - looking and reading tones of tutorials and stuff here is what I have :
//the Add Button in the upper right corner
Button addBookmark = (Button) findViewById(R.id.Button);
addBookmark.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View mapView) {
Intent addBookmarkIntent = new Intent(GoogleMapsApp.this, LocationBookmaker.class);
startActivity(addBookmarkIntent);
}
});
Also I've edited the manifest file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
...
<activity android:name=".LocationBookmarker"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
...
</manifest>
No matter what I try I always get the "The Application GoogleMapsApp (process google.maps.app) has stopped unexpectedly. Please try again." with the only "Force close" option.
I've been trying since two days now - and in a lot of examples in the Internet other say it should be working like this. I cannot see where could be my mistake.
Maybe in the starting of the intent, or the manifest or where...?
According to the exception, the class it's looking for is 'LocationBookmaker', but in your manifest you have 'LocationBookmarker' (notice the 'r'). That may be your problem.
I don't know what the problem you get is but a tip is to run the "Dalvik Debug Monitor" (ddms) on your computer, with that you can capture all exceptions in your application and see exaclly what the error is (most of the time).
You find the ddms in the tools directory of your android installation, if you run windows its a bat file, ddms.bat, that you just run from cmd.
/Viktor
Hello i'm building a custom application.
PRE:
The app will be restricted to a limited amount of user. ( let's say 20 people )
The app should be unique per each user so each apk will refer to a user only.
Each app should have a different package name
So i was starting to think a building script that takes the user list and creates 20 apks ( one for each user ) and updates the strings.xml file with the custom modification needed per-user.
But i really don't know where to start. Is there a good way or a tutorial where i can refer to ?
Just to be clear i'd like to have a manifest like this:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="$pname"
android:versionCode="1"
android:versionName="1.0">
<application>...</application>
</manifest>
And then replace the $pname using ant.
Perhaps you are looking for the aapt command. It has an additional flag --rename-manifest-package. The help says
Rewrite the manifest so that its package name is the package name
given here. Relative class names (for example .Foo) will be
changed to absolute names with the old package so that the code
does not need to change.
I would be using 20 different semi-empty default activities (one per each of users) located in 20 different packages extending 1 real activity, like:
mypackage.user01.MyMainActivity extends mypackage.MyMainActivity;
mypackage.user02.MyMainActivity extends mypackage.MyMainActivity;
mypackage.user03.MyMainActivity extends mypackage.MyMainActivity;
...
mypackage.user20.MyMainActivity extends mypackage.MyMainActivity;
And then 20 different androidmanifests.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="mypackage.userXX">
<application
android:icon="#drawable/MyIcon"
android:label="#string/MyApplication"
>
<activity android:name=".MyActivity"
android:label="#string/MyActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="mypackage.MyActivity"
android:label="#string/MyActivity"/>
<activity
android:name="mypackage.user01.MyActivity"
android:label="#string/MyActivity"/>
<!-- etc. till user20 -->
</application>
</manifest>
which will be copied using simple Ant copy tasks to real one - it's easy to implement, though a bit boring :)
It might be simpler if you store 20 codes in your app, and give one code to each customer. Then just give each customer the same application. The application changes its properties depending on what code was entered.
If this sounds like the right approach, I'll see if I can code some specific aspects. If it isn't the approach you're after, please could you give me a bit more information on how these 20 people would be identified?
You could also use some unique device-identifier. There was an intersting article about that on the Android Developers Blog.
If you want to create a build-script, you can use Ant.