Skip to content

Commit

Permalink
Release React Native for Android
Browse files Browse the repository at this point in the history
This is an early release and there are several things that are known
not to work if you're porting your iOS app to Android.

See the Known Issues guide on the website.

We will work with the community to reach platform parity with iOS.
  • Loading branch information
Martin Konicek committed Sep 14, 2015
1 parent c372dab commit 42eb546
Show file tree
Hide file tree
Showing 571 changed files with 44,550 additions and 116 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Expand Up @@ -25,6 +25,12 @@ project.xcworkspace
# OS X
.DS_Store

# Android/IJ
.idea
.gradle
local.properties
*.iml

# Node
node_modules
*.log
Expand Down
4 changes: 2 additions & 2 deletions Examples/Movies/Movies/AppDelegate.m
Expand Up @@ -37,14 +37,14 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
* on the same Wi-Fi network.
*/

jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/Movies/MoviesApp.ios.bundle?platform=ios&dev=true"];
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/Movies/MoviesApp.ios.includeRequire.runModule.bundle"];

/**
* OPTION 2
* Load from pre-bundled file on disk. To re-generate the static bundle, `cd`
* to your Xcode project folder in the terminal, and run
*
* $ curl 'http://localhost:8081/Examples/Movies/MoviesApp.includeRequire.runModule.bundle' -o main.jsbundle
* $ curl 'http://localhost:8081/Examples/Movies/MoviesApp.ios.includeRequire.runModule.bundle' -o main.jsbundle
*
* then add the `main.jsbundle` file to your project and uncomment this line:
*/
Expand Down
94 changes: 94 additions & 0 deletions Examples/Movies/MoviesApp.android.js
@@ -0,0 +1,94 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @providesModule MoviesApp
* @flow
*/
'use strict';

var React = require('react-native');
var {
AppRegistry,
BackAndroid,
Navigator,
StyleSheet,
ToolbarAndroid,
View,
} = React;

var MovieScreen = require('./MovieScreen');
var SearchScreen = require('./SearchScreen');

var _navigator;
BackAndroid.addEventListener('hardwareBackPress', () => {
if (_navigator && _navigator.getCurrentRoutes().length > 1) {
_navigator.pop();
return true;
}
return false;
});

var RouteMapper = function(route, navigationOperations, onComponentRef) {
_navigator = navigationOperations;
if (route.name === 'search') {
return (
<SearchScreen navigator={navigationOperations} />
);
} else if (route.name === 'movie') {
return (
<View style={{flex: 1}}>
<ToolbarAndroid
actions={[]}
navIcon={require('image!android_back_white')}
onIconClicked={navigationOperations.pop}
style={styles.toolbar}
titleColor="white"
title={route.movie.title} />
<MovieScreen
style={{flex: 1}}
navigator={navigationOperations}
movie={route.movie}
/>
</View>
);
}
};

var MoviesApp = React.createClass({
render: function() {
var initialRoute = {name: 'search'};
return (
<Navigator
style={styles.container}
initialRoute={initialRoute}
configureScene={() => Navigator.SceneConfigs.FadeAndroid}
renderScene={RouteMapper}
/>
);
}
});

var styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
},
toolbar: {
backgroundColor: '#a9a9a9',
height: 56,
},
});

AppRegistry.registerComponent('MoviesApp', () => MoviesApp);

module.exports = MoviesApp;
104 changes: 104 additions & 0 deletions Examples/Movies/SearchBar.android.js
@@ -0,0 +1,104 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @providesModule SearchBar
* @flow
*/
'use strict';

var React = require('react-native');
var {
Image,
Platform,
ProgressBarAndroid,
TextInput,
StyleSheet,
TouchableNativeFeedback,
View,
} = React;

var IS_RIPPLE_EFFECT_SUPPORTED = Platform.Version >= 21;

var SearchBar = React.createClass({
render: function() {
var loadingView;
if (this.props.isLoading) {
loadingView = (
<ProgressBarAndroid
styleAttr="Large"
style={styles.spinner}
/>
);
} else {
loadingView = <View style={styles.spinner} />;
}
var background = IS_RIPPLE_EFFECT_SUPPORTED ?
TouchableNativeFeedback.SelectableBackgroundBorderless() :
TouchableNativeFeedback.SelectableBackground();
return (
<View style={styles.searchBar}>
<TouchableNativeFeedback
background={background}
onPress={() => this.refs.input && this.refs.input.focus()}>
<View>
<Image
source={require('image!android_search_white')}
style={styles.icon}
/>
</View>
</TouchableNativeFeedback>
<TextInput
ref="input"
autoCapitalize="none"
autoCorrect={false}
autoFocus={true}
onChange={this.props.onSearchChange}
placeholder="Search a movie..."
placeholderTextColor="rgba(255, 255, 255, 0.5)"
onFocus={this.props.onFocus}
style={styles.searchBarInput}
/>
{loadingView}
</View>
);
}
});

var styles = StyleSheet.create({
searchBar: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#a9a9a9',
height: 56,
},
searchBarInput: {
flex: 1,
fontSize: 20,
fontWeight: 'bold',
color: 'white',
height: 50,
padding: 0,
backgroundColor: 'transparent'
},
spinner: {
width: 30,
height: 30,
},
icon: {
width: 24,
height: 24,
marginHorizontal: 8,
},
});

module.exports = SearchBar;
35 changes: 35 additions & 0 deletions Examples/Movies/android/app/build.gradle
@@ -0,0 +1,35 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion 22
buildToolsVersion "23.0.1"

defaultConfig {
applicationId "com.facebook.react.movies"
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.2.0'

// Depend on pre-built React Native
compile 'com.facebook.react:react-native:0.11.+'

// Depend on React Native source.
// This is useful for testing your changes when working on React Native.
// compile project(':ReactAndroid')
}
17 changes: 17 additions & 0 deletions Examples/Movies/android/app/proguard-rules.pro
@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
21 changes: 21 additions & 0 deletions Examples/Movies/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.facebook.react.movies">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@drawable/rotten_tomatoes_icon"
android:theme="@style/AppTheme">
<activity
android:name=".MoviesActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>

</manifest>
@@ -0,0 +1,89 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package com.facebook.react.movies;

import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;

import com.facebook.react.LifecycleState;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactRootView;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.shell.MainReactPackage;

public class MoviesActivity extends Activity implements DefaultHardwareBackBtnHandler {

private ReactInstanceManager mReactInstanceManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("MoviesApp.android.bundle")
.setJSMainModuleName("Examples/Movies/MoviesApp.android")
.addPackage(new MainReactPackage())
.setUseDeveloperSupport(true)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();

((ReactRootView) findViewById(R.id.react_root_view))
.startReactApplication(mReactInstanceManager, "MoviesApp", null);
}

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
mReactInstanceManager.showDevOptionsDialog();
return true;
}
return super.onKeyUp(keyCode, event);
}

@Override
protected void onPause() {
super.onPause();

if (mReactInstanceManager != null) {
mReactInstanceManager.onPause();
}
}

@Override
protected void onResume() {
super.onResume();

if (mReactInstanceManager != null) {
mReactInstanceManager.onResume(this);
}
}

@Override
public void onBackPressed() {
if (mReactInstanceManager != null) {
mReactInstanceManager.onBackPressed();
} else {
super.onBackPressed();
}
}

@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

11 comments on commit 42eb546

@kmagiera
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's happening :)

@brunoksato
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thaaaaanks !

@ajkamel
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Congrats on the release! @amasad looking forward to checking this out.

@thibauts
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing work guys ! Thank you !

@AkeemMcLennon
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is OS X still a requirement for Android? I understand why it might be necessary for iOS, but it seems odd when the android build tools are cross platform.

If this is no longer the case, the documentation may need an update.

@kmagiera
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We support OS X for now only as there is a bunch of scripts in our dev tools that rely on running on OS X #2684

@usergit
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

congrats to the react team. When can we expect a stable version any timelines?

@YannickDot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you ! I love what you guys are doing with React.

@bguiz
Copy link

@bguiz bguiz commented on 42eb546 Sep 15, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1
I'm happy it is finally here!

@RnMss
Copy link

@RnMss RnMss commented on 42eb546 Jul 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

              <Text style={{backgroundColor: 'red'}}>
                星际争霸是世界上最好的游戏。星际争霸是世界上最好的游戏。星际争霸是世界上最好的游戏。星际争霸是世界上最好的游戏。
              </Text>

Why is Starcraft the best game in the world? 😆 😆

@mkonicek
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RnMss Probably because that example was built by @andreicoman11 who likes Starcraft 😂

Please sign in to comment.