Nomes de aplicativos diferentes para diferentes sabores de compilação?

90

Tenho 2 sabores de compilação, digamos, flavour1 e flavour2 .

Gostaria que meu aplicativo fosse nomeado, digamos, " AppFlavor1 " ao construir para o sabor1 e " AppFlavor2 " ao construir para o sabor 2.

Não é o título das atividades que desejo alterar. Desejo alterar o nome do aplicativo conforme ele é exibido no menu do telefone e em outros lugares.

Desde build.gradleI pode configurar vários parâmetros para os meus sabores, mas, ao que parece, não o rótulo aplicativo. E não posso alterar o rótulo do aplicativo programaticamente com base em alguma variável também.

Então, como as pessoas lidam com isso?

Alexander Kulyakhtin
fonte

Respostas:

24

Em vez de alterar seu string.xml principal com um script e arriscar bagunçar seu controle de origem, por que não confiar no comportamento de mesclagem padrão da compilação do Android Gradle?

Meu build.gradlecontém

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'
    }
}

Agora posso definir minha app_namestring no variants/[release|debug]/res/strings.xml. E qualquer outra coisa que eu queira mudar também!

Pierre-Luc Paour
fonte
Editado: use res.srcDir para adicionar à matriz em vez de redefini-la completamente.
Pierre-Luc Paour
Sim, eu também descobri isso agora. Muito obrigado
Alexander Kulyakhtin
1
Onde se deve adicionar o sourceSetsbloco? Está embaixo do android{...}bloco?
Abel Callejo
@AbelMelquiadesCallejo dentro do androidbloco de fato.
Pierre-Luc Paour
254

Remover app_namede strings.xml(caso contrário, o gradle reclamará de recursos duplicados). Em seguida, modifique o arquivo de compilação assim:

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

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

Certifique-se também de que o @string/app_namevalor seja atribuído ao android:labelatributo no manifesto.

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

Isso é menos perturbador do que criar novos strings.xmlem diferentes conjuntos construídos ou escrever scripts personalizados.

Ganesh Krishnan
fonte
9
Isso funciona perfeitamente, mas pergunta: como adicionamos strings localizadas para outras localidades ao adicionar o recurso de string como este?
AndroidMechanic - Viral Patel
postou uma pergunta para isso aqui: stackoverflow.com/questions/36105494/… Você se importaria de dar uma olhada, por favor?
AndroidMechanic - Viral Patel
3
Que tal usar várias dimensões de sabor? Como posso dar um nome diferente para cada combinação de sabor / sabor?
Zocket
1
Tudo está bem, mas o nome do aplicativo mostrado abaixo do ícone do iniciador não muda conforme definido pelo sabor do produto gradle. Eu removi "app_name" de strings.xml e configurei no sabor do produto. Se eu vir o nome do aplicativo na página de informações do aplicativo do Android, mostrando o nome do aplicativo definido por variação do produto, mas sob o ícone do iniciador mostrando o nome do meu aplicativo anterior.
iamcrypticcoder
2
@ mahbub.kuet você está usando @string/app_namepara sua activitygravadora de iniciador ? Lembre-se de que do labeliniciador principal activityé usado em vez de application labelpara o nome de exibição do aplicativo.
user650881
13

Se você deseja manter a localização para o nome do aplicativo em diferentes sabores, pode obtê-lo da seguinte maneira:

1) Especifique android:labelem <application>disponível da AndroidManifest.xmlseguinte forma:

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

2) Especifique o valor padrão para appLabelno nível do aplicativo build.gradle:

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

3) Substitua o valor dos sabores do produto da seguinte forma:

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

}

4) Adicione recursos de string para cada uma das Strings (defaultName, flavour1, flavour2) em seu strings.xml. Isso permitirá que você os localize.

Sagar
fonte
A etapa 2 deve estar no nível 'defaultConfig' do build.gradle, NÃO no nível 'android', caso contrário, você obterá um erro de propriedade desconhecido relacionado ao nome manifestPlaceholders.
Brendon Whateley
8

Você pode adicionar um arquivo de recurso de strings a cada variação e, em seguida, usar esses arquivos de recurso para alterar o nome do seu aplicativo. Por exemplo, em um dos meus aplicativos, tenho uma versão gratuita e paga. Para renomeá-los como "Lite" e "Pro", criei um meta_data.xmlarquivo, acrescentei meu app_namevalor a esse XML e o removi strings.xml. Em seguida, app/srccrie uma pasta para cada sabor (veja abaixo a estrutura de exemplo). Dentro desses diretórios, adicione res/values/<string resource file name>. Agora, quando você compilar, este arquivo será copiado em sua compilação e seu aplicativo será renomeado.

Estrutura do arquivo:

app/src
   /pro/res/values/meta_data.xml
   /lite/res/values/meta_data.xml
James
fonte
1
Isso é melhor do que declarar em arquivo gradle. Funciona como um encanto. Obrigado.
Filip Luchianenco
7

Outra opção que eu realmente uso é alterar o manifesto de cada aplicativo. Em vez de copiar a pasta de recursos, você pode criar um manifesto para cada tipo.

sourceSets {
  main {
 }

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

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

Você precisa ter um AndroidManifest principal em seu src main que será o principal. Então você pode definir um manifesto com apenas algumas opções para cada sabor, como (src / release / AndroidManifest.xml):

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

Para depuração, AndroidManifest (src / debug / AndroidManifest.xml):

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

O compilador fará uma mesclagem do manifesto e você pode ter um ícone para cada sabor.

Juanma Jurado
fonte
4

Isso pode ser facilmente realizado em 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
    }
}

Apenas certifique-se de remover app_name de strings.xml

Droid Chris
fonte
2

Em primeiro lugar, responda a esta pergunta: "O usuário pode instalar as duas versões do seu aplicativo no mesmo dispositivo?"

Eu uso um script Python que corrige a fonte. Ele contém algumas funções reutilizáveis ​​e, é claro, o conhecimento do que precisa ser corrigido neste projeto específico. Portanto, o script é específico do aplicativo.

Há muitos patches, os dados para patches são mantidos em um dicionário Python (incluindo nomes de pacotes de aplicativos, eles são diferentes do nome do pacote Java), um dicionário por variação.

Quanto a l10n, as strings podem apontar para outras strings, por exemplo, no meu código eu tenho:

<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
fonte
É uma espécie de versões Light vs Pro, acho que o usuário pode muito bem ter as duas
Alexander Kulyakhtin
Em seguida, você terá que alterar o nome do pacote do aplicativo, por exemplo, com.example.mysoft.pro vs com.example.mysoft.light , caso contrário, o usuário poderá instalar apenas um deles no mesmo dispositivo.
18446744073709551615
Eu fiz isso, mas como obtenho "Meu aplicativo" versus "Meu aplicativo Pro" no menu do telefone? Agora tenho 2 aplicativos (leve e profissional), mas ambos têm o mesmo nome. Preciso de 2 nomes de aplicativos distintos para isso e, como você está dizendo, não é tão fácil?
Alexander Kulyakhtin
Como a outra resposta (votação negativa) menciona, em AndroidManifest.xml existe <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" >. Depois de limpar e reconstruir tudo (duas vezes, uma por sabor), você deve obter dois apk-s com nomes de pacotes Android diferentes e rótulos de iniciador diferentes. BTW, o Eclipse geralmente não detecta mudanças nos recursos, então você tem que pedir para limpar.
18446744073709551615
Como faço para tornar string / app_name diferente por sabor? Sinto muito, mas ainda não consigo entender.
Alexander Kulyakhtin
0

Como faço para tornar string / app_name diferente por sabor?

Eu queria escrever uma atualização, mas percebi que é maior do que a resposta original, dizendo que uso um script Python que corrige o código-fonte.

O script Python tem um parâmetro, um nome de diretório. Esse diretório contém ativos por tipo, recursos como ícones de inicialização e o arquivo properties.txt com um dicionário Python.

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

O script Python carrega o dicionário desse arquivo e substitui o valor entre <string name="app_name">e </string>pelo valor de properties['appTitle'].

O código a seguir é fornecido no estado em que se encontra, 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 ler propriedades de um ou mais desses arquivos:

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

e a função fileReplace é:

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+"'")

As primeiras linhas do script são:

#!/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
fonte
-4

No arquivo AndroidManifest, na tag do aplicativo, você tem esta linha:

android:label

E aí você pode dizer como o rótulo do aplicativo aparecerá no menu de aplicativos do dispositivo

assassino de aluguel
fonte
Sim, eu li a pergunta ... Você poderia definir 2 branches e para cada um (um para flavour1 e um para flavour2) definir um android: label diferente em seu arquivo AndroidManifest.
Hitman
6
Ah, eu não sabia disso. Como coloco esses 2 arquivos de manifesto? Achei que não fosse possível
Alexander Kulyakhtin