-4

My Android app source code includes this line:

import lfreytag.TideNowOR.R;

In order to use this source code in different projects, I would like eliminate the package name from the import, since I have to change it each project. I am using Android Studio. In other apps based on this source code, the package name last part varies such as TideNowOR, TideNowWA, TideNowCA, etc.

As background, I am trying to place all version-specific information inside the RAW directory as simple text files. So I can generate a new version with just changing the manifest and dumping in new collection of text files. Thanks.

user1644002
  • 3,211
  • 3
  • 19
  • 21
  • Here is Exactly what I am looking for: a java code sequence to replace the import statement shown above. – user1644002 Oct 18 '15 at 13:10
  • I don't think it's a worthy goal to not have that import in your code. It's called namespace and it's essential in every programming language: https://en.wikipedia.org/wiki/Namespace. I also don't get what you mean with "have to change it each project"? There's no need to change anything, the Android build tools do everything for you. If you use the same code in different apps, make it a library and you're all set, the build tools will automatically repackage the app using the package name from the manifest, so really I don't see the issue here but maybe I'm missing something? – Emanuel Moecklin Oct 21 '15 at 00:20
  • Also the place to put version/flavor specific configuration is the build file (including package name, version number, app name and more). You might want to read up on this: developer.android.com/tools/building/configuring-gradle.html. Flavors seem to match what you call apps. They can share code, they can have flavor specific code, they can use flavor specific libraries/dependencies, assets, raw files, resource files including drawables, strings, layouts and more. – Emanuel Moecklin Oct 21 '15 at 01:39
  • thank you Emanuel I will accept your answer as the best. – user1644002 Oct 21 '15 at 17:01
  • Why not just compile a java Library or AAR package, and then import it as a new module inside your project? you can name each version of your library, and just replace the library into your apps (versioning different libraries will prevent you from breaking older projects that uses a legacy library accidentally). – Angel Koh Oct 21 '15 at 17:09
  • Answer by Jschools. (see below) allows me to use just one version of source code and lets me deal with the different apps, by using a feature of Gradle to split package name to two uses. So, yes, there IS a way to keep the java code identical across all numerous apps. – user1644002 Oct 24 '15 at 13:16

6 Answers6

4

You could package it up into an Android Library Project (AAR). That will let you share your code, and resources, across different programs.

Buddy
  • 10,874
  • 5
  • 41
  • 58
4

Say hello to Android Studio Flavors!

Not only that you can create multiple builds with one command, but for each build you can specify its own package name and folder structure.

I would speak more about this but there are plenty already who done this all over the site.

Here is a great link: Using Build Flavors - Structuring source folders and build.gradle correctly

Community
  • 1
  • 1
Ilya Gazman
  • 31,250
  • 24
  • 137
  • 216
3

Long story short, don't worry about changing the Java package name. In fact, I would remove any specific identifiers from it so that it is generic, like com.lfreytag.tidenow.

When the new Gradle build system was introduced, they added the possibility to split the package name into two uses. One is the Java package name: this is the one that you reference in all of your Java code in import statements, and it makes up the directory structure of where you keep your .java files. The other use is the name that identifies your app to the Android package manager, which is now called applicationId. The two can be different, so that you can use the same code.

The R file is generated according to what you have set in AndroidManifest.xml as "package". This should be your more generic Java package name. In your app's build.gradle, set applicationId to the specific identifier that you want the Android OS to know about, which would probably include the name of the state that you are building for. More information on this can be found here: http://tools.android.com/tech-docs/new-build-system/applicationid-vs-packagename

The cool thing about Gradle is that you can use flavors to set the applicationId for you, and it can be smart enough to include the correct files in the build process if you set it up right.

For example, in build.gradle, define your different flavors inside the android.productFlavors node like this:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

    defaultConfig {
        applicationId "com.lfreytag.tidenow"
        minSdkVersion 7
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }

    productFlavors {
        wa {
            applicationId "com.lfreytag.tidenow.wa"
        }

        or {
            applicationId "com.lfreytag.tidenow.or"
        }

        ca {
            applicationId "com.lfreytag.tidenow.ca"
        }

        ... etc ...
    }

    buildTypes {
        ...
    }
}

dependencies {
    ...
}

Then, in your src directory, create new subdirectories named the same as the flavors, such as src/wa, src/or, src/ca, and etc. Within each of these directories, place your flavor-specific java, res, assets, and other directories.

When you pick your flavor to build, either via the Build Variants panel in Android Studio or via gradle, such as ./gradlew assembleWaDebug, Gradle will be smart enough to override the values and files in src/main with the more specific ones in your flavor directories.

Jschools
  • 2,698
  • 1
  • 17
  • 18
  • Hey you nailed my question on the head. Thanks, Jschools. I will follow this prescription. Was wondering why there seemed to be two similar but different sets of manifest information. Now I know. – user1644002 Oct 23 '15 at 14:15
  • You could use an [applicationIdSuffix](https://developer.android.com/studio/build/application-id.html#change_the_application_id_for_build_variants) to improve that code a little – P Kuijpers Jan 05 '17 at 15:25
  • @Jschools - Excellent answer. I had assumed that if a product flavor had a different applicationId, then all source code would have to be re-packaged. In all the documentation available about product flavors, no one explained this point as well as you. – EJK Mar 01 '19 at 06:00
2

When using java, remember that the package is the namespace that practically allows the programmer to keep your code organized. Please check this oracle's link to learn what it is and why you need it.

Now, is it possible to not use the package in the source code? May be, but surely is not very convenient, as you would have to tell the compiler where to find all of the classes you need, that would imply to set up a very heavy classpath(learn about class path here).

It may be possible that you do this for very simple java apps, but who knows what kind of ton of work you would need to do for a more complex app, like an android app.

In conclusion.. if it is not broken, don't try to fix it.

Jorch914
  • 3,465
  • 2
  • 16
  • 21
  • Jorch, here are the first two lines of the includes. package lfreytag.TideNowLocal; import lfreytag.TideNowOR.R; I only want to get ride of the second one. – user1644002 Oct 05 '15 at 15:58
  • this is an Android project right? The thing is, the R class is contained in the package lfreytag.TideNowOr, so you can import this package, or remove the import and access it directly whenever you need it. How ever in Android, If I'm not mistaken, I think the IDE should load the R class to the class path. If not, you can access it through the package, and ask for the resource without importing it. – Jorch914 Oct 05 '15 at 16:05
  • Jorch, I guess my question was unclear. I have several apps that are the same except the regional data is unique, and the .APK name is unique. I don't want the apk name to appear in my source code which is what the import statement requires. This is what I am trying to fix. If I have to edit the java for each app, that creates its own set of problems and I want to avoid it. If the app could ask could programmatically what is the package name, that would fix it. – user1644002 Oct 07 '15 at 11:31
  • @user1644002 I get it.You can't do that, the package name will always be there, you can use obscure package names if you wan't, but remember that apks are easily unpackable, However, The code is automatically "obscured" when compiled and packaged into an apk. What is safe, are the names, classes, objects, etc.. you will use in your source code, however a person with a little bit of knowledge of the Android OS, will know that package names are easily found sicne the PackageManager and ActivityManager services(and others) need them in order to work in Android. But this is another subject. – Jorch914 Oct 07 '15 at 15:59
0

First, it's not too difficult to create a script that will go through your files and change the package name. So if you really just want to change the package name, I would run a script to change the package name and viola, you have a new app.

However, I would suggest this approach for your situation:

  1. Create an Android Studio project
  2. Make your present "application" a "library" module
  3. Have as many "application" modules as you want inherit that library
  4. The strings.xml in the "applications" will override the strings.xml in the library.

So, instead of trying to put the strings into the Raw directory, use the strings.xml as Android was designed. The Android Gradle compiler is pretty intelligent. If you have the same string defined in a strings.xml in the library module and in the app module, it will pick the app module's strings. That way you can implement multiple applications with different strings.

EDIT:

Another possible solution would be to include all of the files in the project and then switch which files are displayed for each individual app based on a criterion – like the app's name or package name. There is a Context.getPackageName() but that cannot be used in an import statement.

Another EDIT:

Basically, by choosing to put the files into the "raw" directory, you are tying the files to the package.R import. If you used the assets folder, you could get around that.

Leo Landau
  • 1,785
  • 17
  • 25
  • thanks Leo. I have such numerous long text files in the raw directory, the formats dictated by available public data. These are unique from app to app. Placing them all into an xml resource is unreasonable and a lot harder than my problem I have to solve. Basically I am saying. I will have 30 to so apps. Each has same functional code. Just different contents of raw directory. I don't want 30 copies of the same source set, each different in only one line is all. thanks again Leo. – user1644002 Oct 14 '15 at 22:58
  • @user1644002 placing the strings into an xml file may seem unreasonable for your project, but that's the Android way of solving the issue. The other solution as you are aware is to make copies of the source code, which may also seem unreasonable to you. I'll leave it to you to choose the "least unreasonable" option for your project. Maybe there's another option none of us has thought of yet. – Leo Landau Oct 14 '15 at 23:12
  • @user1644002 I did think of another solution that could work for your project. I added it in the post. – Leo Landau Oct 14 '15 at 23:19
  • @user1644002 I added another possible solution – Leo Landau Oct 14 '15 at 23:27
  • Leo, I could move my raw files into assets instead but I would still have all the common files in res: drawable, layout, menu, and values. So the same dependency would be present. – user1644002 Oct 15 '15 at 15:26
0

I'm not 100% sure what you're asking but I think the question is based on a number of misunderstandings that I'd like to eliminate.

import lfreytag.TideNowOR.R;

Is importing the references to the resources defined in a specific module (http://developer.android.com/sdk/installing/create-project.html#SettingUpLibraryModule). These resources include everything in the res folder (strings, ids, drawables, layouts etc.).

This import has to include the package name because that name is the namespace for your code. Namespaces are an essential element of all modern programming languages to avoid name collisions between multiple identifiers that share the same name.

The way to use common code in different projects (assuming you use Android Studio and Gradle to build) is to:

  1. Create a separate project and add that projects as a dependency to your different apps, see e.g. here: Android studio add external project to build.gradle
  2. Create a library module and reference that module in your app module: http://developer.android.com/sdk/installing/create-project.html#SettingUpLibraryModule
  3. Use flavors to use different package names and "version-specific information"

In the first two cases the build tool will take care of "converting" your library lfreytag.TideNowOR package name to your app package name lfreytag.TideNowWA or whatever you need. It will basically create an aar package for reuse in other projects/modules.

From your description option 3 seems to be the best option and I'd like to explain how you could structure your project to be able to have the common code and different "apps".

enter image description here

The screenshot shows one of my projects that has a main node which would be your lfreytag.TideNowOR code you want to reuse in your different "apps". It can contain all the elements of a typical Android project like the actual Java code, resource files and files in the assets folder.

The app has different flavors and those flavors map to what you refer to as "apps" (in the screenshots the flavors are the free, pro and pro_amazon nodes).

A flavor is a variation of your app and can define its own package name, have its own manifest, set variables to be used in the manifest (e.g. the app name or ContentProvider authorities) or in your code (flags to distinguish different flavors, flavor specific strings etc.).

To use different package names in the manifest for the different flavors (or your "apps") you'd define the applicationId in the build file like so:

    defaultConfig {
        applicationId "lfreytag.TideNowOR"
    }

    productFlavors {
        wa {
            applicationId "lfreytag.TideNowWA"
        }
        ca {
            applicationId 'lfreytag.TideNowCA'
        }
// .. more flavors
    }

To use different app names for the different flavors you'd have:

defaultConfig {
    manifestPlaceholders = [appName: "Tide Now Oregon"]
}
wa {
    manifestPlaceholders = [appName: "Tide Now Washington"]
}

in the build file and the following the the manifest:

<application
    android:name="MyApp"
    android:label="${appName}"

As mentioned before you can do much more than that but this should suffice as an example. Quintessential is that the build file is the place to put flavor or "app" specific configuration while all flavor specific files go into the respective folder (raw files, asset files, resource files, code, manifest). When the application is built, each flavor is built into an apk and so the flavors indeed become your "apps".

Community
  • 1
  • 1
Emanuel Moecklin
  • 28,488
  • 11
  • 69
  • 85