Różne nazwy aplikacji dla różnych smaków kompilacji?


94

Mam 2 smaki kompilacji, powiedzmy smak1 i smak2 .

Chciałbym, aby moja aplikacja nosiła nazwę, na przykład „ AppFlavor1 ”, gdy tworzę dla smaku 1 i „ AppFlavor2 ”, gdy tworzę dla smaku 2.

To nie jest tytuł działań, które chcę zmienić. Chcę zmienić nazwę aplikacji wyświetlaną w menu telefonu i w innych miejscach.

Od build.gradlemogę ustawić różne parametry dla moich smaków, ale wydaje się, że nie etykieta aplikacji. Nie mogę też programowo zmienić etykiety aplikacji na podstawie jakiejś zmiennej.

Jak więc ludzie sobie z tym radzą?

Odpowiedzi:


24

Zamiast zmieniać plik main strings.xml za pomocą skryptu i ryzykować zepsucie kontroli źródła, dlaczego nie polegać na standardowym zachowaniu kompilacji dla Androida Gradle?

Moje build.gradlezawiera

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

Więc teraz mogę zdefiniować mój app_nameciąg w variants/[release|debug]/res/strings.xml. I wszystko, co chcę zmienić!


Edytowane: użyj res.srcDir, aby dodać do tablicy zamiast całkowicie ją przedefiniowywać.
Pierre-Luc Paour

Tak, do tej pory też to rozgryzłem.
Wielkie

1
Gdzie należy dodać sourceSetsblok? Czy to pod android{...}blokiem?
Abel Callejo

@AbelMelquiadesCallejo androidrzeczywiście wewnątrz bloku.
Pierre-Luc Paour

261

Usuń app_namez strings.xml(w przeciwnym razie gradle będzie narzekać na zduplikowane zasoby). Następnie zmodyfikuj plik kompilacji w następujący sposób:

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

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

Upewnij się również, że @string/app_namewartość jest przypisana do android:labelatrybutu w manifeście.

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

Jest to mniej uciążliwe niż tworzenie nowych strings.xmlw innych zestawach zbudowanych lub pisanie niestandardowych skryptów.


9
To działa jak urok, ale pytanie: w jaki sposób dodajemy zlokalizowane ciągi dla innych lokalizacji podczas dodawania zasobu ciągu w ten sposób?
AndroidMechanic - Viral Patel

opublikował pytanie w tej sprawie tutaj: stackoverflow.com/questions/36105494/… Czy możesz rzucić okiem?
AndroidMechanic - Viral Patel

3
A co z używaniem wielu wymiarów smaku? Jak mogę nazwać to inaczej dla każdej kombinacji smaku / smaku?
Zocket

1
Wszystko jest w porządku, ale nazwa aplikacji wyświetlona poniżej ikony programu uruchamiającego nie zmienia się zgodnie z ustawieniem smaku produktu. Usunąłem „app_name” z strings.xml i ustawiłem smak produktu. Jeśli widzę nazwę aplikacji na stronie Informacje o aplikacji w systemie Android, pokazując nazwę aplikacji ustawioną według smaku produktu, ale pod ikoną programu uruchamiającego pokazującą moją poprzednią nazwę aplikacji.
iamcrypticcoder

2
@ mahbub.kuet używasz @string/app_namejako activityetykiety programu uruchamiającego ? Należy pamiętać, że zamiast nazwy wyświetlanej aplikacji jest używany labelz głównego programu uruchamiającego . activityapplication label
user650881

13

Jeśli chcesz zachować lokalizację nazwy aplikacji w różnych smakach, możesz to osiągnąć w następujący sposób:

1) Określ android:labelw <application>dostępne w AndroidManifest.xmlsposób następujący:

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

2) Określ domyślną wartość fo appLabelna poziomie aplikacji build.gradle:

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

3) Zastąp wartość smaków produktu w następujący sposób:

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

}

4) Dodaj zasoby ciągów dla każdego z ciągów (defaultName, smak1, smak2) w pliku strings.xml. To pozwoli ci je zlokalizować.


Krok # 2 musi znajdować się na poziomie „defaultConfig” pliku build.gradle, NIE na poziomie „android”, w przeciwnym razie zostanie wyświetlony nieznany błąd właściwości związany z nazwą manifestPlaceholder.
Brendon Whateley,

8

Możesz dodać plik zasobów ciągów do każdego smaku, a następnie użyć tych plików zasobów, aby zmienić nazwę aplikacji. Na przykład w jednej z moich aplikacji mam darmową i płatną wersję. Aby zmienić ich nazwy na „Lite” i „Pro”, utworzyłem meta_data.xmlplik, dodałem swoją app_namewartość do tego XML i usunąłem go z strings.xml. Następnie app/srcutwórz folder dla każdego smaku (zobacz poniżej przykładową strukturę). W tych katalogach dodaj res/values/<string resource file name>. Teraz podczas kompilacji ten plik zostanie skopiowany do Twojej kompilacji, a nazwa aplikacji zostanie zmieniona.

Struktura plików:

app/src
   /pro/res/values/meta_data.xml
   /lite/res/values/meta_data.xml

1
Jest to lepsze niż deklarowanie w pliku gradle. Działa jak marzenie. Dziękuję Ci.
Filip Luchianenco

7

Inną opcją, której faktycznie używam, jest zmiana manifestu dla każdej aplikacji. Zamiast kopiować folder zasobów, możesz utworzyć manifest dla każdego smaku.

sourceSets {
  main {
 }

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

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

Musisz mieć główny plik AndroidManifest w pliku głównym src, który będzie głównym. Następnie możesz zdefiniować manifest z tylko niektórymi opcjami dla każdego smaku, takimi jak (src / release / AndroidManifest.xml):

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

Do debugowania, AndroidManifest (src / debug / AndroidManifest.xml):

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

Kompilator połączy manifest i możesz mieć ikonę dla każdego smaku.


4

Można to łatwo osiągnąć w 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
    }
}

Po prostu upewnij się, że usunąłeś app_name z strings.xml


2

Przede wszystkim odpowiedz na pytanie: „Czy użytkownik może zainstalować obie wersje aplikacji na tym samym urządzeniu?”

Używam skryptu w Pythonie, który łata źródło. Zawiera kilka funkcji wielokrotnego użytku i oczywiście wiedzę, co należy załatać w tym konkretnym projekcie. Tak więc skrypt jest specyficzny dla aplikacji.

Jest dużo łatania, dane do łatania są przechowywane w słowniku Pythona (w tym nazwy pakietów aplikacji, przy okazji różnią się od nazwy pakietu Java), po jednym słowniku na smak.

Co do l10n, stringi mogą wskazywać na inne stringi, np. W moim kodzie mam:

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

To rodzaj wersji Light vs Pro, myślę, że użytkownik może mieć je obie
Alexander Kulyakhtin

Następnie musisz zmienić nazwę pakietu aplikacji, np. Com.example.mysoft.pro vs com.example.mysoft.light , w przeciwnym razie użytkownik będzie mógł zainstalować tylko jeden z nich na tym samym urządzeniu.
18446744073709551615

Zrobiłem to, ale jak mogę zobaczyć „Moja aplikacja” i „Moja aplikacja Pro” w menu telefonu? Mam teraz 2 aplikacje (light i pro), ale obie mają tę samą nazwę. Potrzebuję do tego 2 różnych nazw aplikacji, a to, jak mówisz, nie jest takie proste?
Alexander Kulyakhtin

Jak wspomina druga (obniżona) odpowiedź, w AndroidManifest.xml jest <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > . Po wyczyszczeniu i ponownym skompilowaniu wszystkiego (dwa razy, raz na smak), powinieneś otrzymać dwa pliki APK z różnymi nazwami pakietów Androida i różnymi etykietami programu uruchamiającego. BTW, Eclipse często nie wykrywa zmian w zasobach, więc musisz poprosić go o wyczyszczenie.
18446744073709551615

Jak jednak zmienić ciąg / nazwa_aplikacji w zależności od smaku? Przepraszam, wciąż nie mogę tego zrozumieć.
Alexander Kulyakhtin

0

Jak jednak zmienić ciąg / nazwa_aplikacji w zależności od smaku?

Chciałem napisać aktualizację, ale zdałem sobie sprawę, że jest ona większa niż oryginalna odpowiedź mówiąca, że ​​używam skryptu Pythona, który łata źródło.

Skrypt w Pythonie ma parametr, nazwę katalogu. Ten katalog zawiera zasoby według smaku, zasoby, takie jak ikony programu uruchamiającego oraz plik properties.txt ze słownikiem Pythona.

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

Skrypt w języku Python ładuje słownik z tego pliku i zastępuje wartość między <string name="app_name">i </string>wartością properties['appTitle'].

Poniższy kod jest dostarczany na zasadzie „tak jak jest / jak było” itp.

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_/@\- ]+")

aby odczytać właściwości z jednego lub więcej takich plików:

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

a funkcja fileReplace to:

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

Pierwsze wiersze skryptu to:

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

-4

W pliku AndroidManifest w tagu aplikacji znajduje się następujący wiersz:

android:label

I tam możesz powiedzieć, jak etykieta aplikacji pojawi się w menu aplikacji na urządzeniu


Tak, przeczytałem pytanie ... Możesz ustawić 2 gałęzie i dla każdej z nich (jedną dla flavour1 i jedną dla flavour2) ustawić inną etykietę android: label w pliku AndroidManifest.
Hitman

6
Ach, nie wiedziałem tego. Jak jednak umieścić te 2 pliki manifestu? Myślałem, że to niemożliwe
Alexander Kulyakhtin
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.