Skip to content

Commit

Permalink
android: Receive and handle shares from other apps.
Browse files Browse the repository at this point in the history
Enables handling and receiving shares from other apps, for any
type of content (text, images, and other files).

Uses the new root component, `SharingRoot` and the new native
module `SharingModule`. Introduces a new route for sharing -
`SharingScreen` allowing sharing content via PMs or to Streams.

`react-navigation` code Suggested-by: Chris Bobbe <[email protected]>

Closes  #M117.
  • Loading branch information
agrawal-d committed May 29, 2020
1 parent 81fa373 commit 22dd05c
Show file tree
Hide file tree
Showing 10 changed files with 642 additions and 7 deletions.
6 changes: 1 addition & 5 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,16 @@
</intent-filter>
</activity>

<!-- Disabled while the feature is experimental. See #117 and #4124.
<activity
android:name=".sharing.ReceiveShareActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:label="@string/app_name"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize">
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
</activity>
-->

<!-- When `react-native run-android` learns from the decoy `package`
attribute in our comment above that the application ID is
Expand Down
6 changes: 5 additions & 1 deletion src/boot/AppEventHandlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
NotificationListener,
notificationOnAppActive,
} from '../notification';
import { ShareReceivedListener, handleInitialShare } from '../sharing';
import { appOnline, appOrientation, initSafeAreaInsets } from '../actions';
import PresenceHeartbeat from '../presence/PresenceHeartbeat';

Expand Down Expand Up @@ -98,6 +99,7 @@ class AppEventHandlers extends PureComponent<Props> {
};

notificationListener = new NotificationListener(this.props.dispatch);
shareListener = new ShareReceivedListener(this.props.dispatch);

handleMemoryWarning = () => {
// Release memory here
Expand All @@ -106,7 +108,7 @@ class AppEventHandlers extends PureComponent<Props> {
componentDidMount() {
const { dispatch } = this.props;
handleInitialNotification(dispatch);

handleInitialShare(dispatch);
this.netInfoDisconnectCallback = NetInfo.addEventListener(this.handleConnectivityChange);
AppState.addEventListener('change', this.handleAppStateChange);
AppState.addEventListener('memoryWarning', this.handleMemoryWarning);
Expand All @@ -116,6 +118,7 @@ class AppEventHandlers extends PureComponent<Props> {
// $FlowFixMe: libdef wrongly says callback's parameter is optional
Orientation.addOrientationListener(this.handleOrientationChange);
this.notificationListener.start();
this.shareListener.start();
}

componentWillUnmount() {
Expand All @@ -128,6 +131,7 @@ class AppEventHandlers extends PureComponent<Props> {
// $FlowFixMe: libdef wrongly says callback's parameter is optional
Orientation.removeOrientationListener(this.handleOrientationChange);
this.notificationListener.stop();
this.shareListener.stop();
}

render() {
Expand Down
2 changes: 2 additions & 0 deletions src/nav/AppNavigator.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import TopicListScreen from '../topics/TopicListScreen';
import EmojiPickerScreen from '../emoji/EmojiPickerScreen';
import LegalScreen from '../settings/LegalScreen';
import UserStatusScreen from '../user-status/UserStatusScreen';
import SharingScreen from '../sharing/SharingScreen';

export default createStackNavigator(
// $FlowFixMe react-navigation types :-/ -- see a36814e80
Expand Down Expand Up @@ -65,6 +66,7 @@ export default createStackNavigator(
notifications: { screen: NotificationsScreen },
legal: { screen: LegalScreen },
'user-status': { screen: UserStatusScreen },
sharing: { screen: SharingScreen },
},
{
initialRouteName: 'main',
Expand Down
4 changes: 4 additions & 0 deletions src/nav/navActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
Narrow,
UserOrBot,
ApiResponseServerSettings,
SharedData,
} from '../types';
import { getSameRoutesCount } from '../selectors';

Expand Down Expand Up @@ -103,3 +104,6 @@ export const navigateToLegal = (): NavigationAction => StackActions.push({ route

export const navigateToUserStatus = (): NavigationAction =>
StackActions.push({ routeName: 'user-status' });

export const navigateToSharing = (sharedData: SharedData): NavigationAction =>
StackActions.push({ routeName: 'sharing', params: { sharedData } });
5 changes: 5 additions & 0 deletions src/nav/navReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ const rehydrate = (state, action) => {
return getStateForRoute('loading');
}

// Dont switch to main UI if sharing screen is on.
if (state.routes.find(route => route.routeName === 'sharing')) {
return state;
}

// Great: we have an active, logged-in account, and server data for it.
// Show the main UI.
return getStateForRoute('main');
Expand Down
44 changes: 44 additions & 0 deletions src/sharing/ChooseRecipientsScreen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* @flow strict-local */
import React, { PureComponent } from 'react';
import type { User, Dispatch } from '../types';
import { connect } from '../react-redux';
import { Screen } from '../common';
import UserPickerCard from '../user-picker/UserPickerCard';

type Props = $ReadOnly<{|
dispatch: Dispatch,
onComplete: (User[]) => void,
|}>;

type State = {|
filter: string,
|};

class ChooseRecipientsScreen extends PureComponent<Props, State> {
state = {
filter: '',
};

handleFilterChange = (filter: string) => this.setState({ filter });

handleComplete = (selected: Array<User>) => {
const { onComplete } = this.props;
onComplete(selected);
};

render() {
const { filter } = this.state;
return (
<Screen
search
scrollEnabled={false}
searchBarOnChange={this.handleFilterChange}
canGoBack={false}
>
<UserPickerCard filter={filter} onComplete={this.handleComplete} />
</Screen>
);
}
}

export default connect<{||}, _, _>()(ChooseRecipientsScreen);
Loading

0 comments on commit 22dd05c

Please sign in to comment.