Skip to content

Commit 42eb546

Browse files
author
Martin Konicek
committed
Release React Native for Android
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.
1 parent c372dab commit 42eb546

571 files changed

Lines changed: 44550 additions & 116 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ project.xcworkspace
2525
# OS X
2626
.DS_Store
2727

28+
# Android/IJ
29+
.idea
30+
.gradle
31+
local.properties
32+
*.iml
33+
2834
# Node
2935
node_modules
3036
*.log

Examples/Movies/Movies/AppDelegate.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,14 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
3737
* on the same Wi-Fi network.
3838
*/
3939

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

4242
/**
4343
* OPTION 2
4444
* Load from pre-bundled file on disk. To re-generate the static bundle, `cd`
4545
* to your Xcode project folder in the terminal, and run
4646
*
47-
* $ curl 'http://localhost:8081/Examples/Movies/MoviesApp.includeRequire.runModule.bundle' -o main.jsbundle
47+
* $ curl 'http://localhost:8081/Examples/Movies/MoviesApp.ios.includeRequire.runModule.bundle' -o main.jsbundle
4848
*
4949
* then add the `main.jsbundle` file to your project and uncomment this line:
5050
*/
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/**
2+
* The examples provided by Facebook are for non-commercial testing and
3+
* evaluation purposes only.
4+
*
5+
* Facebook reserves all rights not expressly granted.
6+
*
7+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
8+
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9+
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
10+
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
11+
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
12+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13+
*
14+
* @providesModule MoviesApp
15+
* @flow
16+
*/
17+
'use strict';
18+
19+
var React = require('react-native');
20+
var {
21+
AppRegistry,
22+
BackAndroid,
23+
Navigator,
24+
StyleSheet,
25+
ToolbarAndroid,
26+
View,
27+
} = React;
28+
29+
var MovieScreen = require('./MovieScreen');
30+
var SearchScreen = require('./SearchScreen');
31+
32+
var _navigator;
33+
BackAndroid.addEventListener('hardwareBackPress', () => {
34+
if (_navigator && _navigator.getCurrentRoutes().length > 1) {
35+
_navigator.pop();
36+
return true;
37+
}
38+
return false;
39+
});
40+
41+
var RouteMapper = function(route, navigationOperations, onComponentRef) {
42+
_navigator = navigationOperations;
43+
if (route.name === 'search') {
44+
return (
45+
<SearchScreen navigator={navigationOperations} />
46+
);
47+
} else if (route.name === 'movie') {
48+
return (
49+
<View style={{flex: 1}}>
50+
<ToolbarAndroid
51+
actions={[]}
52+
navIcon={require('image!android_back_white')}
53+
onIconClicked={navigationOperations.pop}
54+
style={styles.toolbar}
55+
titleColor="white"
56+
title={route.movie.title} />
57+
<MovieScreen
58+
style={{flex: 1}}
59+
navigator={navigationOperations}
60+
movie={route.movie}
61+
/>
62+
</View>
63+
);
64+
}
65+
};
66+
67+
var MoviesApp = React.createClass({
68+
render: function() {
69+
var initialRoute = {name: 'search'};
70+
return (
71+
<Navigator
72+
style={styles.container}
73+
initialRoute={initialRoute}
74+
configureScene={() => Navigator.SceneConfigs.FadeAndroid}
75+
renderScene={RouteMapper}
76+
/>
77+
);
78+
}
79+
});
80+
81+
var styles = StyleSheet.create({
82+
container: {
83+
flex: 1,
84+
backgroundColor: 'white',
85+
},
86+
toolbar: {
87+
backgroundColor: '#a9a9a9',
88+
height: 56,
89+
},
90+
});
91+
92+
AppRegistry.registerComponent('MoviesApp', () => MoviesApp);
93+
94+
module.exports = MoviesApp;
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/**
2+
* The examples provided by Facebook are for non-commercial testing and
3+
* evaluation purposes only.
4+
*
5+
* Facebook reserves all rights not expressly granted.
6+
*
7+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
8+
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9+
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
10+
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
11+
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
12+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13+
*
14+
* @providesModule SearchBar
15+
* @flow
16+
*/
17+
'use strict';
18+
19+
var React = require('react-native');
20+
var {
21+
Image,
22+
Platform,
23+
ProgressBarAndroid,
24+
TextInput,
25+
StyleSheet,
26+
TouchableNativeFeedback,
27+
View,
28+
} = React;
29+
30+
var IS_RIPPLE_EFFECT_SUPPORTED = Platform.Version >= 21;
31+
32+
var SearchBar = React.createClass({
33+
render: function() {
34+
var loadingView;
35+
if (this.props.isLoading) {
36+
loadingView = (
37+
<ProgressBarAndroid
38+
styleAttr="Large"
39+
style={styles.spinner}
40+
/>
41+
);
42+
} else {
43+
loadingView = <View style={styles.spinner} />;
44+
}
45+
var background = IS_RIPPLE_EFFECT_SUPPORTED ?
46+
TouchableNativeFeedback.SelectableBackgroundBorderless() :
47+
TouchableNativeFeedback.SelectableBackground();
48+
return (
49+
<View style={styles.searchBar}>
50+
<TouchableNativeFeedback
51+
background={background}
52+
onPress={() => this.refs.input && this.refs.input.focus()}>
53+
<View>
54+
<Image
55+
source={require('image!android_search_white')}
56+
style={styles.icon}
57+
/>
58+
</View>
59+
</TouchableNativeFeedback>
60+
<TextInput
61+
ref="input"
62+
autoCapitalize="none"
63+
autoCorrect={false}
64+
autoFocus={true}
65+
onChange={this.props.onSearchChange}
66+
placeholder="Search a movie..."
67+
placeholderTextColor="rgba(255, 255, 255, 0.5)"
68+
onFocus={this.props.onFocus}
69+
style={styles.searchBarInput}
70+
/>
71+
{loadingView}
72+
</View>
73+
);
74+
}
75+
});
76+
77+
var styles = StyleSheet.create({
78+
searchBar: {
79+
flexDirection: 'row',
80+
alignItems: 'center',
81+
backgroundColor: '#a9a9a9',
82+
height: 56,
83+
},
84+
searchBarInput: {
85+
flex: 1,
86+
fontSize: 20,
87+
fontWeight: 'bold',
88+
color: 'white',
89+
height: 50,
90+
padding: 0,
91+
backgroundColor: 'transparent'
92+
},
93+
spinner: {
94+
width: 30,
95+
height: 30,
96+
},
97+
icon: {
98+
width: 24,
99+
height: 24,
100+
marginHorizontal: 8,
101+
},
102+
});
103+
104+
module.exports = SearchBar;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
apply plugin: 'com.android.application'
2+
3+
android {
4+
compileSdkVersion 22
5+
buildToolsVersion "23.0.1"
6+
7+
defaultConfig {
8+
applicationId "com.facebook.react.movies"
9+
minSdkVersion 16
10+
targetSdkVersion 22
11+
versionCode 1
12+
versionName "1.0"
13+
ndk {
14+
abiFilters "armeabi-v7a", "x86"
15+
}
16+
}
17+
buildTypes {
18+
release {
19+
minifyEnabled false
20+
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21+
}
22+
}
23+
}
24+
25+
dependencies {
26+
compile fileTree(dir: 'libs', include: ['*.jar'])
27+
compile 'com.android.support:appcompat-v7:22.2.0'
28+
29+
// Depend on pre-built React Native
30+
compile 'com.facebook.react:react-native:0.11.+'
31+
32+
// Depend on React Native source.
33+
// This is useful for testing your changes when working on React Native.
34+
// compile project(':ReactAndroid')
35+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Add project specific ProGuard rules here.
2+
# By default, the flags in this file are appended to flags specified
3+
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4+
# You can edit the include path and order by changing the proguardFiles
5+
# directive in build.gradle.
6+
#
7+
# For more details, see
8+
# http://developer.android.com/guide/developing/tools/proguard.html
9+
10+
# Add any project specific keep options here:
11+
12+
# If your project uses WebView with JS, uncomment the following
13+
# and specify the fully qualified class name to the JavaScript interface
14+
# class:
15+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16+
# public *;
17+
#}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2+
package="com.facebook.react.movies">
3+
4+
<uses-permission android:name="android.permission.INTERNET" />
5+
6+
<application
7+
android:allowBackup="true"
8+
android:label="@string/app_name"
9+
android:icon="@drawable/rotten_tomatoes_icon"
10+
android:theme="@style/AppTheme">
11+
<activity
12+
android:name=".MoviesActivity">
13+
<intent-filter>
14+
<action android:name="android.intent.action.MAIN" />
15+
<category android:name="android.intent.category.LAUNCHER" />
16+
</intent-filter>
17+
</activity>
18+
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
19+
</application>
20+
21+
</manifest>
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* The examples provided by Facebook are for non-commercial testing and
3+
* evaluation purposes only.
4+
*
5+
* Facebook reserves all rights not expressly granted.
6+
*
7+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
8+
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9+
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
10+
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
11+
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
12+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13+
*/
14+
15+
package com.facebook.react.movies;
16+
17+
import android.app.Activity;
18+
import android.os.Bundle;
19+
import android.view.KeyEvent;
20+
21+
import com.facebook.react.LifecycleState;
22+
import com.facebook.react.ReactInstanceManager;
23+
import com.facebook.react.ReactRootView;
24+
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
25+
import com.facebook.react.shell.MainReactPackage;
26+
27+
public class MoviesActivity extends Activity implements DefaultHardwareBackBtnHandler {
28+
29+
private ReactInstanceManager mReactInstanceManager;
30+
31+
@Override
32+
protected void onCreate(Bundle savedInstanceState) {
33+
super.onCreate(savedInstanceState);
34+
setContentView(R.layout.activity_main);
35+
36+
mReactInstanceManager = ReactInstanceManager.builder()
37+
.setApplication(getApplication())
38+
.setBundleAssetName("MoviesApp.android.bundle")
39+
.setJSMainModuleName("Examples/Movies/MoviesApp.android")
40+
.addPackage(new MainReactPackage())
41+
.setUseDeveloperSupport(true)
42+
.setInitialLifecycleState(LifecycleState.RESUMED)
43+
.build();
44+
45+
((ReactRootView) findViewById(R.id.react_root_view))
46+
.startReactApplication(mReactInstanceManager, "MoviesApp", null);
47+
}
48+
49+
@Override
50+
public boolean onKeyUp(int keyCode, KeyEvent event) {
51+
if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
52+
mReactInstanceManager.showDevOptionsDialog();
53+
return true;
54+
}
55+
return super.onKeyUp(keyCode, event);
56+
}
57+
58+
@Override
59+
protected void onPause() {
60+
super.onPause();
61+
62+
if (mReactInstanceManager != null) {
63+
mReactInstanceManager.onPause();
64+
}
65+
}
66+
67+
@Override
68+
protected void onResume() {
69+
super.onResume();
70+
71+
if (mReactInstanceManager != null) {
72+
mReactInstanceManager.onResume(this);
73+
}
74+
}
75+
76+
@Override
77+
public void onBackPressed() {
78+
if (mReactInstanceManager != null) {
79+
mReactInstanceManager.onBackPressed();
80+
} else {
81+
super.onBackPressed();
82+
}
83+
}
84+
85+
@Override
86+
public void invokeDefaultOnBackPressed() {
87+
super.onBackPressed();
88+
}
89+
}
237 Bytes
Loading
575 Bytes
Loading

0 commit comments

Comments
 (0)