¿Diferentes nombres de aplicaciones para diferentes versiones de compilación?

94

Tengo 2 sabores de construcción, digamos, sabor1 y sabor2 .

Me gustaría que mi aplicación se llamara, digamos, " AppFlavor1 " cuando construyo para el sabor1 y " AppFlavor2 " cuando construyo para el sabor 2.

No es el título de las actividades que quiero cambiar. Quiero cambiar el nombre de la aplicación como se muestra en el menú del teléfono y en otros lugares.

Desde build.gradlepuedo establecer varios parámetros para mis sabores pero, al parecer, no la etiqueta de la aplicación. Y tampoco puedo cambiar la etiqueta de la aplicación mediante programación en función de alguna variable.

Entonces, ¿cómo maneja la gente esto?

Alexander Kulyakhtin
fuente

Respuestas:

24

En lugar de cambiar su strings.xml principal con un script y arriesgarse a estropear su control de código fuente, ¿por qué no confiar en el comportamiento de fusión estándar de la compilación de Android Gradle?

Mi build.gradlecontiene

sourceSets {
    main {
        manifest.srcFile 'AndroidManifest.xml'
        java.srcDirs = ['src']
        resources.srcDirs = ['src']
        aidl.srcDirs = ['src']
        renderscript.srcDirs = ['src']
        res.srcDirs = ['res']
        assets.srcDirs = ['assets']
    }

    release {
        res.srcDir 'variants/release/res'
    }

    debug {
        res.srcDir 'variants/debug/res'
    }
}

Entonces ahora puedo definir mi app_namecadena en el variants/[release|debug]/res/strings.xml. ¡Y también quiero cambiar cualquier otra cosa!

Pierre-Luc Paour
fuente
Editado: use res.srcDir para agregar a la matriz en lugar de redefinirla por completo.
Pierre-Luc Paour
Sí, ya me he dado cuenta de eso también. Muchas gracias
Alexander Kulyakhtin
1
¿Dónde se debe agregar el sourceSetsbloque? ¿Está debajo del android{...}bloque?
Abel Callejo
@AbelMelquiadesCallejo dentro de la androidcuadra de hecho.
Pierre-Luc Paour
262

Eliminar app_namede strings.xml(de lo contrario, gradle se quejará de recursos duplicados). Luego modifique el archivo de compilación de esta manera:

productFlavors {
    flavor1{
        resValue "string", "app_name", "AppNameFlavor1"
    }

    flavor2{
        resValue "string", "app_name", "AppNameFlavor2"
    }
   } 

También asegúrese de @string/app_nameque se asigne un valor para el android:labelatributo en el manifiesto.

<application
        ...
        android:label="@string/app_name"
        ...

Esto es menos perturbador que crear nuevos strings.xmlen diferentes conjuntos construidos o escribir scripts personalizados.

Ganesh Krishnan
fuente
9
Esto funciona como un encanto, pero pregunta: ¿cómo agregamos cadenas localizadas para otras configuraciones regionales cuando agregamos el recurso de cadena como este?
AndroidMechanic - Viral Patel
publicó una pregunta para esto aquí: stackoverflow.com/questions/36105494/… ¿Le importaría echar un vistazo, por favor?
AndroidMechanic - Viral Patel
3
¿Qué pasa con el uso de múltiples dimensiones de sabor? ¿Cómo puedo nombrarlo de manera diferente para cada combinación de sabor / sabor?
Zocket
1
Todo está bien, pero el nombre de la aplicación que se muestra debajo del ícono del iniciador no cambia según lo establecido por el sabor del producto gradle. Eliminé "app_name" de strings.xml y configuré el sabor del producto. Si veo el nombre de la aplicación en la página de información de la aplicación de Android, que muestra el nombre de la aplicación establecido por el tipo de producto, pero debajo del icono de inicio que muestra el nombre de mi aplicación anterior
iamcrypticcoder
2
@ mahbub.kuet, ¿estás usando @string/app_namepara tu activityetiqueta de lanzador ? Tenga en cuenta que el labeldel lanzador principal activityse usa con preferencia al del nombre application labelpara mostrar de la aplicación.
user650881
13

Si desea mantener la localización del nombre de la aplicación en diferentes sabores, puede lograrlo de la siguiente manera:

1) Especificar android:labelen <application>disponible en AndroidManifest.xmlla siguiente forma:

<application
    ...
    android:label="${appLabel}"
    ...
>

2) Especifique el valor predeterminado fo appLabelen el nivel de la aplicación build.gradle:

manifestPlaceholders = [appLabel:"@string/defaultName"]

3) Anule el valor de los sabores de los productos de la siguiente manera:

productFlavors {
    AppFlavor1 {
        manifestPlaceholders = [appLabel:"@string/flavor1"]
    }
    AppFlavor2 {
        manifestPlaceholders = [appLabel:"@string/flavor2"]
    }

}

4) Agregue recursos de cadena para cada una de las cadenas (nombre predeterminado, sabor1, sabor2) en su strings.xml. Esto le permitirá localizarlos.

Sagar
fuente
El paso # 2 debe estar en el nivel 'defaultConfig' de build.gradle NO en el nivel 'android', de lo contrario, obtendrá un error de propiedad desconocido relacionado con el nombre de manifestPlaceholders.
Brendon Whateley
8

Puede agregar un archivo de recursos de cadenas a cada sabor y luego usar esos archivos de recursos para cambiar el nombre de su aplicación. Por ejemplo, en una de mis aplicaciones, tengo una versión gratuita y de pago. Para renombrarlos "Lite" y "Pro", creé un meta_data.xmlarchivo, agregué mi app_namevalor a ese XML y lo eliminé de strings.xml. A continuación, app/srccree una carpeta para cada sabor (consulte a continuación la estructura de ejemplo). Dentro de estos directorios, agregue res/values/<string resource file name>. Ahora, cuando compile, este archivo se copiará en su compilación y su aplicación cambiará de nombre.

Estructura de archivo:

app/src
   /pro/res/values/meta_data.xml
   /lite/res/values/meta_data.xml
James
fuente
1
Esto es mejor que declarar en un archivo gradle. Funciona de maravilla. Gracias.
Filip Luchianenco
7

Otra opción que realmente utilizo es cambiar el manifiesto para cada aplicación. En lugar de copiar la carpeta de recursos, puede crear un manifiesto para cada sabor.

sourceSets {
  main {
 }

  release {
    manifest.srcFile 'src/release/AndroidManifest.xml'
 }

  debug {
    manifest.srcFile 'src/debug/AndroidManifest.xml'
 }
}

Debe tener un AndroidManifest principal en su src main que será el principal. Luego puede definir un manifiesto con solo algunas opciones para cada sabor como (src / release / AndroidManifest.xml):

<manifest package="com.application.yourapp">
  <application android:icon="@drawable/ic_launcher">
  </application>
</manifest>

Para la depuración, AndroidManifest (src / debug / AndroidManifest.xml):

<manifest package="com.application.yourapp">
  <application android:icon="@drawable/ic_launcher2">
  </application>
</manifest>

El compilador fusionará el manifiesto y puede tener un icono para cada sabor.

Juanma Jurado
fuente
4

Esto se puede lograr fácilmente en buildTypes

buildTypes {
    debug {
        buildConfigField("String", "server_type", "\"TEST\"")
        resValue "string", "app_name", "Eventful-Test"
        debuggable true
        signingConfig signingConfigs.debug_key_sign
    }

    stage {
        buildConfigField("String", "server_type", "\"STAGE\"")
        resValue "string", "app_name", "Eventful-Stage"
        debuggable true
        signingConfig signingConfigs.debug_key_sign
    }

    release {
        buildConfigField("String", "server_type", "\"PROD\"")
        resValue "string", "app_name", "Eventful"
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        //TODO - add release signing
    }
}

Solo asegúrese de eliminar app_name de strings.xml

El droide Chris
fuente
2

En primer lugar, responda esta pregunta: "¿Puede el usuario instalar ambos tipos de su aplicación en el mismo dispositivo?"

Utilizo un script de Python que parchea la fuente. Contiene algunas funciones reutilizables y, por supuesto, conocimiento de lo que se necesita parchear en este proyecto en particular. Entonces, el script es específico de la aplicación.

Hay muchos parches, los datos para parchear se guardan en un diccionario de Python (incluidos los nombres de los paquetes de aplicaciones, por cierto, son diferentes del nombre del paquete de Java), un diccionario por sabor.

En cuanto a l10n, las cadenas pueden apuntar a otras cadenas, por ejemplo, en mi código tengo:

<string name="app_name">@string/x_app_name_xyz</string>

<string name="x_app_name_default">My Application</string>
<string name="x_app_name_xyz">My App</string>
18446744073709551615
fuente
Es una especie de versiones Light vs Pro, creo que el usuario puede tener ambas
Alexander Kulyakhtin
Luego, tendrá que cambiar el nombre del paquete de la aplicación, por ejemplo, com.example.mysoft.pro vs com.example.mysoft.light , de lo contrario, el usuario podrá instalar solo uno de ellos en el mismo dispositivo.
18446744073709551615
Hice eso, pero ¿cómo obtengo "Mi aplicación" frente a "Mi aplicación Pro" en el menú del teléfono? Ahora tengo 2 aplicaciones (ligera y profesional) pero ambas tienen el mismo nombre. Necesito 2 nombres de aplicaciones distintos para eso y, como dices, ¿no es tan fácil?
Alexander Kulyakhtin
Como menciona la otra respuesta (votada en contra), en AndroidManifest.xml hay <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" >. Después de limpiar y reconstruir todo (dos veces, una por sabor), debe obtener dos apk-s con diferentes nombres de paquetes de Android y diferentes etiquetas de inicio. Por cierto, Eclipse a menudo no detecta cambios en los recursos, por lo que debe pedirle que limpie.
18446744073709551615
Sin embargo, ¿cómo puedo hacer que string / app_name sea diferente por sabor? Lo siento, todavía no puedo conseguirlo.
Alexander Kulyakhtin
0

Sin embargo, ¿cómo puedo hacer que string / app_name sea diferente por sabor?

Quería escribir una actualización, pero me di cuenta de que es más grande que la respuesta original que dice que uso un script de Python que parchea la fuente.

La secuencia de comandos de Python tiene un parámetro, un nombre de directorio. Ese directorio contiene activos por sabor, recursos como íconos de iniciador y el archivo properties.txt con un diccionario de Python.

{ 'someBoolean' : True
, 'someParam' : 'none'
, 'appTitle' : '@string/x_app_name_xyz'
}

La secuencia de comandos de Python carga el diccionario desde ese archivo y reemplaza el valor entre <string name="app_name">y </string>por el valor de properties['appTitle'].

El siguiente código se proporciona tal cual está / como estaba, etc.

for strings_xml in glob.glob("res/values*/strings.xml"):
    fileReplace(strings_xml,'<string name="app_name">',properties['appTitle'],'</string>',oldtextpattern=r"[a-zA-Z0-9_/@\- ]+")

para leer las propiedades de uno o más de estos archivos:

with open(filename1) as f:
    properties = eval(f.read())
with open(filename2) as f:
    properties.update(eval(f.read()))

y la función fileReplace es:

really = True
#False for debugging

# In the file 'fname',
# find the text matching "before oldtext after" (all occurrences) and
# replace 'oldtext' with 'newtext' (all occurrences).
# If 'mandatory' is true, raise an exception if no replacements were made.
def fileReplace(fname,before,newtext,after,oldtextpattern=r"[\w.]+",mandatory=True):
    with open(fname, 'r+') as f:
        read_data = f.read()
        pattern = r"("+re.escape(before)+r")"+oldtextpattern+"("+re.escape(after)+r")"
        replacement = r"\g<1>"+newtext+r"\g<2>"
        new_data,replacements_made = re.subn(pattern,replacement,read_data,flags=re.MULTILINE)
        if replacements_made and really:
            f.seek(0)
            f.truncate()
            f.write(new_data)
            if verbose:
                print "patching ",fname," (",replacements_made," occurrence" + ("s" if 1!=replacements_made else ""),")",newtext,("-- no changes" if new_data==read_data else "-- ***CHANGED***")
        elif replacements_made:
            print fname,":"
            print new_data
        elif mandatory:
            raise Exception("cannot patch the file: "+fname+" with ["+newtext+"] instead of '"+before+"{"+oldtextpattern+"}"+after+"'")

Las primeras líneas del guión son:

#!/usr/bin/python
# coding: utf-8

import sys
import os
import re
import os.path
import shutil
import argparse
import string
import glob
from myutils import copytreeover
18446744073709551615
fuente
-4

En el archivo AndroidManifest, en la etiqueta de la aplicación tienes esta línea:

android:label

Y allí puede decir cómo aparecerá la etiqueta de la aplicación en el menú de aplicaciones del dispositivo

Sicario
fuente
Sí, leí la pregunta ... Puede establecer 2 ramas y para cada una (una para sabor1 y otra para sabor2) establecer una etiqueta diferente de android: en su archivo AndroidManifest.
Hitman
6
Ah, no lo sabía. Sin embargo, ¿cómo coloco esos 2 archivos de manifiesto? Pensé que no es posible
Alexander Kulyakhtin