Monthly Archives: April 2020

Missing Generated Signed Bundle APK menu item in Android Studio

If you’ve just switched to the latest version of Android Studio (3.6), and you don’t see any option to generate a signed APK, you should take a look at the “Build: Sync” window in the IDE. Let’s look at such a case, here:

https://www.fullstackoasis.com/images/2020-03-23-android-studio-build-error-clouddoorbell.png

Here we have an Android Studio project, but we cannot see the “Generate Signed Bundle / APK…” menu item. How did this happen?

In this case, I opened Android Studio, and closed all projects. Then, I clicked the link to “Import an Android code sample”. I chose Cloud Doorbell, and when the project opened, I saw a number of errors. You can see those errors in the screenshot above. I wanted to build an APK, but when I clicked the “Build” menu, that menu item was missing. It was frustrating because there was no mention in the menu about what was wrong.

However, under the “Build: Sync” window, I saw:

ERROR: Failed to find Build Tools revision 26.0.2
Install Build Tools 26.0.2 and sync project
Upgrade plugin to version 3.6.1 and sync project

Those two lines were links, and when I clicked the first one, the latest Build Tools were installed. I believe it also did a gradle sync on its own.

So, if you run into this problem, take a look at that “Build: Sync” window. If it’s not open already, you can open it by clicking on the “Build” tab at the bottom of the Android Studio IDE. If you see errors there, fix them before trying to do anything else. Otherwise, you may wind up banging your head against a wall… 🙂

This problem happened to me with the Cloud Doorbell project, but it can happen in other types of projects. Someone on Reddit had the same problem after importing a project from Buildbox.

If you found this interesting, click the subscribe button below! I write a new post about once a week.

How to play an Android audio file in React Native

Here’s a quick tutorial to get you started playing an mp3 file in an Android app that was built using React Native. This tutorial was developed using Linux (Ubuntu 16.04), React Native 0.61.5, and the React Native Sound package 0.11.0.

First, make your project directory. Let’s call it AudioTmp. In a terminal window, do this:

npx react-native init AudioTmp
cd AudioTmp

All of the following commands assume you’ve got your terminal window open, and you’re standing in the root directory of your project (in AudioTmp, where App.js is located).

You’ll need some extra packages, so run these install commands:

npm install typescript@
npm install eslint@^5.0.0
npm install react-native-sound

Next, as described in the React Native Sound documentation, use the link command:

react-native link react-native-sound

This “links” the library where needed into the android and ios directories.

Now add an mp3 file to your Android project:

mkdir android/app/src/main/res/raw
cp john_bartmann_04_earning_happiness.mp3 android/app/src/main/res/raw

You can download a copy of John Bartmann’s “Earning Happiness” mp3 file here. It has a public domain license; let’s thank John Bartmann for making his cool “Earning Happiness” music open source (original source at Free Music Archive)!

Next, make the utils directory under your project root:

mkdir utils

Add a new file called SoundPlayer.js in the utils directory. Here’s the source for that:

/**
 * Really simple demo of https://www.npmjs.com/package/react-native-sound
 */
const Sound = require('react-native-sound');
/**
 * Audio credit:
 * https://freemusicarchive.org/music/John_Bartmann/Public_Domain_Soundtrack_Music_Album_One/earning-happiness
 * "Earning Happiness" by John Bartmann licensed under a CC0 1.0 Universal License
 * https://creativecommons.org/publicdomain/zero/1.0/
 */
const FILENAME = 'john_bartmann_04_earning_happiness.mp3';

class SoundPlayer {
    constructor(props) {
        console.log("SoundPlayer constructor");
    };
    stopSound() {
        console.log('SoundPlayer.stopSound');
        this.whoosh.stop();
        this.whoosh.release();
    }
    playSound() {
        console.log('SoundPlayer.playSound');
        let me = this;
        me.whoosh = new Sound(FILENAME, Sound.MAIN_BUNDLE, error => {
            if (error) {
                console.log('failed to load the sound', error);
                return;
            }
            // loaded successfully
            console.log(
                'duration in seconds: ' +
                me.whoosh.getDuration() +
                'number of channels: ' +
                me.whoosh.getNumberOfChannels()
            );
            me.whoosh.play(success => {
                if (success) {
                console.log('successfully finished playing');
                } else {
                console.log('playback failed due to audio decoding errors');
                }
            });
        });
    }
}
export default SoundPlayer;

Replace the code in App.js with this code:

/**
 * Simple demo of https://www.npmjs.com/package/react-native-sound
 * The app plays an included mp3 file when the user clicks on 'Play' button.
 * It stops the playback when the user clicks on the 'Stop' button.
 */
import React from 'react';
import {
  StyleSheet,
  View,
    Text,
  Button
} from 'react-native';

import {
  Colors
} from 'react-native/Libraries/NewAppScreen';

/** Keep all sound functionality in separate utils area */
import SoundPlayer from './utils/SoundPlayer';

class App extends React.Component {
    constructor(props) {
        super(props);
        this.soundPlayer = new SoundPlayer();
    }

    onPlayPress() {
        console.log('onPlayPress method');
        this.soundPlayer.playSound();
    }

    onStopPress() {
        console.log('onPlayPress method');
        this.soundPlayer.stopSound();
    }

    render() {
        return (
        <View style={styles.body}>
            <Text style={styles.sectionTitle}>Click button to Play</Text>
            <View style={styles.buttonStyle}>
                <Button style={styles.buttonStyle}
                    onPress={this.onPlayPress.bind(this)}
                    title="Play"
                />
            </View>
            <Text style={styles.sectionTitle}>Click button to Stop</Text>
            <View style={styles.buttonStyle}>
                <Button buttonStyle={styles.buttonStyle}
                    onPress={this.onStopPress.bind(this)}
                    title="Stop"
                />
            </View>
        </View>
        );
    }
};

const styles = StyleSheet.create({
    body: {
        backgroundColor: Colors.white,
        flex: 1,
        flexDirection: 'column',
        justifyContent: 'center',
        backgroundColor: 'gainsboro'
    },
    sectionTitle: {
        fontSize: 24,
        fontWeight: '600',
        color: Colors.black,
        padding: 20,
        backgroundColor: 'gainsboro' /* See https://reactnative.dev/docs/colors */,
    },
    buttonStyle: {
        padding: 20,
        backgroundColor: 'gainsboro' /* See https://reactnative.dev/docs/colors */,
    }
});

export default App;

Run the Android Emulator:

emulator -no-snapshot -avd Galaxy_Nexus_API_28

Start your Metro server:

npx react-native start

Install the app in your Android Emulator:

npx react-native run-android

I see these warnings at my command line, but everything seems to work okay:

error React Native CLI uses autolinking for native dependencies, but the following modules are linked manually: 
  - react-native-sound (to unlink run: "react-native unlink react-native-sound")
This is likely happening when upgrading React Native from below 0.60 to 0.60 or above. Going forward, you can unlink this dependency via "react-native unlink <dependency>" and it will be included in your app automatically. If a library isn't compatible with autolinking, disregard this message and notify the library maintainers.
Read more about autolinking: https://github.com/react-native-community/cli/blob/master/docs/autolinking.md
info Running jetifier to migrate libraries to AndroidX. You can disable it using "--no-jetifier" flag.
Jetifier found 875 file(s) to forward-jetify. Using 8 workers...

Then it just works! You can hit the Play and Stop buttons to play the audio.

The source code for this project is available at github.

One remark: when I initially created this project, I didn’t use the link command, because the React Native documentation indicated it was not needed for version 0.60. However, that turned out to be untrue! You can save yourself time by doing it the way I’ve done it in this tutorial. When using link, the demo worked immediately, and I didn’t have to make any changes to gradle files.

Here’s a view of the demo app:

Audio Player using React Native

If you found this interesting, click the subscribe button below! I write a new post about once a week.