/* Stamp the stylesheet with its own modified time. The browser then fetches a new copy the moment the file changes, and keeps caching it the rest of the time — no more hard refreshes after an edit. */ $cssFile = __DIR__ . '/../assets/css/blog.css'; $cssVer = is_file($cssFile) ? filemtime($cssFile) : time(); ?>
Android Apps

How to Reskin an Android App in Android Studio (Step by Step)

Make any Android source code your own: your name, icon, colours, font, text and menu links, step by step with copy-paste Java and Kotlin code. No experience needed.

An Android phone showing an app being restyled with new colours and fonts

Reskinning means taking an app that already works and turning it into your own: a new name, a new icon, your colours and font, your text and your links. The code underneath stays the same. It is how most people start selling apps from purchased source code, and it is also how a lot of apps get rejected from Google Play, because the reskin stopped at the logo.

This guide walks through a full reskin in Android Studio, in the order that avoids broken builds. Every step has the exact file to open, and the code is in both Java and Kotlin. It also covers the part most guides skip: what Google Play checks before it accepts a reskinned app.

What a reskin changes, and what it does not

A reskin changes how the app looks and who it belongs to. It does not change what the app does.

ChangeWhere it lives
Package name (the app’s unique ID)app/build.gradle
App nameres/values/strings.xml
App iconres/mipmap-*
Coloursres/values/colors.xml and themes.xml
Fontres/font/ and the theme
All visible textres/values/strings.xml
Menu links (Privacy Policy, About)res/menu/ and the activity
Splash screenthe theme and MainActivity
Ad and Firebase IDsAndroidManifest.xml, google-services.json

If you only change the logo and the name, Google sees the same app as the original and as every other copy of it. The steps at the end deal with that.

What you need

  • Android Studio, the current stable version.
  • The source code you have the right to rebrand. A purchased licence allows this; an app you downloaded from someone else does not.
  • Your assets: a square logo at least 1024×1024 px with a transparent background, your brand colours as hex codes, and a font file in TTF or OTF.
  • A website page for your privacy policy. Google Play requires one for almost every app.

Step 1: Open the project and get a clean build first

Do not change anything yet. Open the project with File → Open, let Gradle sync finish, and run the app once on a phone or emulator.

If it does not build now, it will not build after a reskin either, and you will not know which change broke it. The two usual problems:

  • Gradle or JDK version errors. Accept the upgrade Android Studio offers, or set the JDK under Settings → Build Tools → Gradle → Gradle JDK to the bundled version.
  • “No matching client found for package name”. The project uses Firebase and the old google-services.json does not match. Leave it for now; Step 10 replaces it.

Once it runs, commit the untouched project to Git or copy the folder. That is your way back.

Step 2: Change the package name

The package name, or applicationId, is the app’s permanent identity on Google Play. Two apps can never share one, so this is not optional: if you keep the original, Play will refuse the upload because that ID already belongs to someone.

Open app/build.gradle.kts (or app/build.gradle in older projects) and change both lines:

android {
    namespace = "com.yourbrand.chat"

    defaultConfig {
        applicationId = "com.yourbrand.chat"
        versionCode = 1
        versionName = "1.0"
    }
}

The Groovy version looks like this:

android {
    namespace 'com.yourbrand.chat'

    defaultConfig {
        applicationId "com.yourbrand.chat"
        versionCode 1
        versionName "1.0"
    }
}

applicationId is what Play sees. namespace is where your R and BuildConfig classes are generated. Keeping them the same avoids confusion.

To rename the Java or Kotlin folders as well, switch the Project panel to Android view, click the gear icon and untick Compact Middle Packages, then right-click each part of the old package and choose Refactor → Rename. This is cosmetic; the app works with the old folder names, but a clean project is easier to maintain. Run Build → Clean Project afterwards.

Step 3: Change the app name

Open res/values/strings.xml and change app_name:

<string name="app_name">BrandChat</string>

Then check AndroidManifest.xml. The android:label on the <application> tag should point to @string/app_name. Some source codes hardcode the name there or on individual activities, so search the whole project with Ctrl+Shift+F for the old name.

Step 4: Change the app icon

Right-click the res folder and choose New → Image Asset. Image Asset Studio builds every icon size and the adaptive icon for you.

  1. Keep Icon type on Launcher Icons (Adaptive and Legacy).
  2. On the Foreground Layer tab, choose your logo and resize it until it sits inside the safe zone circle.
  3. On the Background Layer tab, pick a colour, usually your brand colour or white.
  4. Keep the name ic_launcher so the manifest does not need to change, and click Finish.

Uninstall the old version from your phone before testing. Launchers cache icons, and an old icon after a reinstall is the launcher, not your build.

Google Play also needs a separate 512×512 px PNG icon for the store listing. Export that from the same logo.

Step 5: Change the colours

Colours are set in two places. colors.xml names them, and the theme decides where each one is used. Open res/values/colors.xml and add your brand colours:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="brand_primary">#0D9488</color>
    <color name="brand_primary_dark">#0F766E</color>
    <color name="brand_accent">#F59E0B</color>
    <color name="brand_surface">#F8FAFC</color>
    <color name="white">#FFFFFF</color>
</resources>

Now open res/values/themes.xml. Projects built on Material 3 use the attributes below; change them to your colours:

<style name="Theme.BrandChat" parent="Theme.Material3.DayNight.NoActionBar">
    <item name="colorPrimary">@color/brand_primary</item>
    <item name="colorOnPrimary">@color/white</item>
    <item name="colorPrimaryContainer">@color/brand_primary_dark</item>
    <item name="colorSecondary">@color/brand_accent</item>
    <item name="android:colorBackground">@color/brand_surface</item>
</style>

Three things catch people out here:

  • Dark mode has its own file. Check res/values-night/themes.xml too, or the app keeps the old colours whenever the phone is in dark mode.
  • Hardcoded colours. Older source codes write #FF5722 straight into layouts. Search the project for # inside res/layout and swap them for your @color/ names.
  • The status bar. Apps that target Android 15 (API 35) or newer draw edge to edge, and android:statusBarColor no longer has any effect. The colour behind the status bar comes from your toolbar or top app bar instead.

If the app is built with Jetpack Compose, the colours are in ui/theme/Color.kt and Theme.kt instead of XML:

private val BrandColors = lightColorScheme(
    primary = Color(0xFF0D9488),
    onPrimary = Color.White,
    secondary = Color(0xFFF59E0B),
    background = Color(0xFFF8FAFC)
)

@Composable
fun BrandChatTheme(content: @Composable () -> Unit) {
    MaterialTheme(colorScheme = BrandColors, typography = BrandTypography, content = content)
}

Step 6: Change the font of the whole app

Changing the font once in the theme is far better than editing every TextView. Right-click res and choose New → Android Resource Directory, set the type to font, and copy your font files into it.

File names in res may only use lowercase letters, numbers and underscores. Rename Poppins-Bold.ttf to poppins_bold.ttf before copying it in, or the build fails with an unhelpful error.

Then create res/font/poppins.xml, which groups the weights into one family:

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <font android:fontStyle="normal" android:fontWeight="400"
        android:font="@font/poppins_regular"
        app:fontStyle="normal" app:fontWeight="400"
        app:font="@font/poppins_regular" />

    <font android:fontStyle="normal" android:fontWeight="700"
        android:font="@font/poppins_bold"
        app:fontStyle="normal" app:fontWeight="700"
        app:font="@font/poppins_bold" />
</font-family>

Point the theme at the family. Both lines are needed: the first is read by the platform views, the second by AppCompat and Material components.

<style name="Theme.BrandChat" parent="Theme.Material3.DayNight.NoActionBar">
    <!-- colours from the step above -->
    <item name="android:fontFamily">@font/poppins</item>
    <item name="fontFamily">@font/poppins</item>
</style>

That covers almost every piece of text in the app. If a screen still shows the old font, that view or its style sets its own android:fontFamily or textAppearance; change it there.

For Compose, define the family once and copy it into the typography the theme uses:

val Poppins = FontFamily(
    Font(R.font.poppins_regular, FontWeight.Normal),
    Font(R.font.poppins_bold, FontWeight.Bold)
)

val BrandTypography = Typography().run {
    copy(
        bodyLarge = bodyLarge.copy(fontFamily = Poppins),
        bodyMedium = bodyMedium.copy(fontFamily = Poppins),
        titleLarge = titleLarge.copy(fontFamily = Poppins),
        labelLarge = labelLarge.copy(fontFamily = Poppins)
    )
}

Step 7: Change the text

Every word the user sees should be in res/values/strings.xml. Go through it line by line and rewrite it in your own words: welcome screens, button labels, empty states, error messages. This is also where your app starts to feel different from the original, which matters later for Google Play.

To find text that was typed straight into layouts, run Code → Inspect Code and look for the Hardcoded text warning. Move each one into strings.xml.

If you want the app in Urdu or another language as well, create res/values-ur/strings.xml with the same names and translated values. Android picks the right file from the phone’s language.

Step 8: Add Privacy Policy and About to the menu

This is where you add your own links. A privacy policy link inside the app is good practice and something Google Play reviewers look for, and an About screen tells users whose app this is.

First add the text and the link to strings.xml, so they can be changed later without touching code:

<string name="menu_home">Home</string>
<string name="menu_more">More</string>
<string name="menu_privacy">Privacy Policy</string>
<string name="menu_about">About</string>
<string name="privacy_url">https://yourwebsite.com/privacy/</string>
<string name="about_text">Version %1$s\nMade by Your Brand.</string>

If the app has a navigation drawer

Open the drawer’s menu file, usually res/menu/activity_main_drawer.xml or drawer_menu.xml, and add the two items. Putting them in a submenu gives them their own section under a divider:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <group android:id="@+id/group_main" android:checkableBehavior="single">
        <item android:id="@+id/nav_home"
            android:icon="@drawable/ic_home"
            android:title="@string/menu_home" />
    </group>

    <item android:title="@string/menu_more">
        <menu>
            <item android:id="@+id/nav_privacy"
                android:icon="@drawable/ic_privacy"
                android:title="@string/menu_privacy" />
            <item android:id="@+id/nav_about"
                android:icon="@drawable/ic_info"
                android:title="@string/menu_about" />
        </menu>
    </item>
</menu>

Add the icons with New → Vector Asset, search the clip art for “privacy” and “info”, and name them to match.

The links open in a Custom Tab, which is Chrome inside your app with your colours, so the user never feels sent away. Add the dependency to app/build.gradle.kts:

implementation("androidx.browser:browser:1.8.0")

Then handle the clicks in the activity that owns the drawer. Java:

navigationView.setNavigationItemSelectedListener(item -> {
    int id = item.getItemId();
    if (id == R.id.nav_privacy) {
        openUrl(getString(R.string.privacy_url));
    } else if (id == R.id.nav_about) {
        showAbout();
    }
    drawerLayout.closeDrawers();
    return true;
});

private void openUrl(String url) {
    new CustomTabsIntent.Builder().build().launchUrl(this, Uri.parse(url));
}

private void showAbout() {
    new MaterialAlertDialogBuilder(this)
        .setTitle(R.string.app_name)
        .setMessage(getString(R.string.about_text, BuildConfig.VERSION_NAME))
        .setPositiveButton(android.R.string.ok, null)
        .show();
}

Kotlin:

navigationView.setNavigationItemSelectedListener { item ->
    when (item.itemId) {
        R.id.nav_privacy -> openUrl(getString(R.string.privacy_url))
        R.id.nav_about -> showAbout()
    }
    drawerLayout.closeDrawers()
    true
}

private fun openUrl(url: String) {
    CustomTabsIntent.Builder().build().launchUrl(this, Uri.parse(url))
}

private fun showAbout() {
    MaterialAlertDialogBuilder(this)
        .setTitle(R.string.app_name)
        .setMessage(getString(R.string.about_text, BuildConfig.VERSION_NAME))
        .setPositiveButton(android.R.string.ok, null)
        .show()
}

The Java version uses if rather than switch on purpose. Since Android Gradle Plugin 8, resource IDs are no longer constants, and a switch on R.id values does not compile.

The About dialog shows the version with BuildConfig.VERSION_NAME. New projects do not generate BuildConfig by default, so if it shows in red, switch it on:

android {
    buildFeatures {
        buildConfig = true
    }
}

If the app has a toolbar with a three-dot menu

Create res/menu/main_menu.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item android:id="@+id/action_privacy"
        android:title="@string/menu_privacy"
        app:showAsAction="never" />
    <item android:id="@+id/action_about"
        android:title="@string/menu_about"
        app:showAsAction="never" />
</menu>

And in the activity (Kotlin; the Java version follows the same pattern with if):

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    menuInflater.inflate(R.menu.main_menu, menu)
    return true
}

override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
    R.id.action_privacy -> { openUrl(getString(R.string.privacy_url)); true }
    R.id.action_about -> { showAbout(); true }
    else -> super.onOptionsItemSelected(item)
}

Step 9: Change the splash screen

Since Android 12 the system draws the splash screen itself, from your theme. Older source codes often still have a separate SplashActivity with a timer; that now shows two splash screens in a row. The clean way is the SplashScreen library:

implementation("androidx.core:core-splashscreen:1.0.1")

Add a starting theme to themes.xml and set it on MainActivity in the manifest:

<style name="Theme.BrandChat.Starting" parent="Theme.SplashScreen">
    <item name="windowSplashScreenBackground">@color/brand_primary</item>
    <item name="windowSplashScreenAnimatedIcon">@drawable/ic_splash_logo</item>
    <item name="postSplashScreenTheme">@style/Theme.BrandChat</item>
</style>

Then install it at the top of onCreate:

override fun onCreate(savedInstanceState: Bundle?) {
    installSplashScreen()          // before super.onCreate
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
}

The icon should be a vector or a PNG with plenty of empty space around the logo, because the system crops it into a circle.

Step 10: Replace the ad and Firebase IDs

This step is easy to forget and expensive to get wrong. If you publish with the original developer’s AdMob IDs, the ad money goes to them.

  • AdMob: create your own app in AdMob, then replace the App ID in AndroidManifest.xml and every ad unit ID, which are usually in strings.xml. Use Google’s test IDs while you develop. Our AdMob integration guide covers this in detail.
  • Firebase: create a new Firebase project, add an Android app with your new package name, download its google-services.json and replace the one in the app/ folder. The old file is tied to the old package name and the build will fail with it.
<meta-data
    android:name="com.google.android.gms.ads.APPLICATION_ID"
    android:value="ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy" />

Also search for API keys, server URLs and email addresses the original developer left in the code. If the app talks to a backend, such as the server in our chat app tutorial, point it at yours.

Step 11: Version, sign and build

Set versionCode to 1 and versionName to “1.0” for your first release. Every later update needs a higher versionCode, or Play rejects it.

Build the release with Build → Generate Signed App Bundle or APK → Android App Bundle, and create a new keystore. Do not reuse one that came with the source code. Back the keystore and its passwords up somewhere safe; with Play App Signing you can recover from losing it, but it is a slow process.

Step 12: Make sure Google Play accepts it

Google Play does not allow apps that offer the same experience as apps already in the store, apps built from templates without original content, or many near-identical apps from one developer. A reskin that only changes the logo fits that description exactly, and it is the most common reason reskinned apps are rejected or later removed.

What makes a reskin acceptable is real difference:

  • Your own content. Different categories, data, images, wording or levels, not the demo content that came with the code.
  • At least one feature or change in focus the original does not have, even a small one aimed at a specific audience.
  • A unique store listing: your own description, screenshots of your version, and your own feature graphic. Never copy the original listing.
  • A working privacy policy URL in the Play Console, the same one your menu links to, and an honest Data safety form.
  • One good app, not ten copies. Publishing the same reskin under several names is exactly what the policy targets and can put the whole developer account at risk.

Reskin checklist

CheckDone when
Clean build before changesThe untouched project runs
Package nameapplicationId and namespace are yours
Name and iconThe launcher shows your name and icon after a fresh install
ColoursLight and dark mode both use your colours
FontEvery screen uses your font
TextNo hardcoded text warnings; all wording is yours
MenuPrivacy Policy opens your page; About shows your name and version
SplashOne splash screen, with your logo
IDsYour AdMob IDs and your google-services.json
ReleaseSigned with your own new keystore
Store listingOwn description, screenshots and privacy policy

Mistakes that cost the most time

  • Reskinning before the first clean build. You cannot tell your mistakes from problems the code already had.
  • Changing only namespace. The app still has the old applicationId and Play refuses it.
  • Font files with capital letters or dashes in res/font.
  • Forgetting values-night, so the old colours appear in dark mode.
  • Publishing with someone else’s ad IDs or keystore.

Where to get source code worth reskinning

A reskin is only as good as the code under it. Pick a project that builds on a current Android Gradle Plugin, targets a recent API level, and comes with documentation and support. You can browse ready-made, tested Android app source codes in our shop; each one is a clean starting point for exactly the steps above. If you would rather start from a website, see how to convert a website into an Android app.

Questions people ask

What does it mean to reskin an app?
Reskinning is changing an existing app's branding and look while keeping its code and features: a new package name, app name, icon, colours, font, text and links. It is the usual way to turn purchased source code into your own app.
Is it legal to reskin an app?
It is legal when you have the right to the code, for example a source code licence you bought that allows rebranding. Reskinning an app you copied or decompiled from someone else is copyright infringement, and Google Play removes such apps.
Will Google Play reject a reskinned app?
It can. Google Play's spam policy rejects apps that offer the same experience as existing apps or are built from templates without original content. A reskin that adds its own content, its own store listing and at least some different functionality is far more likely to be accepted.
Do I have to change the package name?
Yes. The applicationId is the app's permanent ID on Google Play and no two apps can share one. If you keep the original package name, the upload is refused because that ID already belongs to another developer.
How do I change the font of the whole Android app?
Put your font files in res/font, create a font family XML that groups the weights, then set android:fontFamily and fontFamily in your app theme. That changes almost every text view at once. In Jetpack Compose, set the font family in the Typography used by MaterialTheme.
How do I add a privacy policy link to my app?
Add a menu item, for example in the navigation drawer or the toolbar's three-dot menu, and open your privacy policy URL with a Custom Tab when it is tapped. Use the same URL in the Privacy Policy field of the Play Console.
How long does it take to reskin an Android app?
A basic reskin of a well-built project, covering name, icon, colours, font, text and menu links, takes a few hours to a day. Adding your own content, new features and a store listing that passes review usually takes a few days.
How much does an app reskin cost?
Doing it yourself costs nothing beyond the source code. Freelance reskin services start at a few dollars for a name and icon change and go much higher for a full redesign; the cheap ones rarely do enough to pass Google Play's review.
Can I reskin an app without coding?
Partly. Names, icons, colours and text are resource files you can edit without writing code. Package names, menu actions, splash screens, ad IDs and Firebase still need Android Studio and small code changes, all shown in this guide.
Should I reskin in Java or Kotlin?
Keep the language the project already uses. Converting a whole project during a reskin adds risk for no visible benefit. This guide gives every code change in both Java and Kotlin.

Written by Habib Baloch

I build Android apps, websites and Ubuntu servers, and write down exactly how I did it.

Ask me about this guide →