Skip to content
Closed
131 changes: 83 additions & 48 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,61 +13,96 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"
tools:node="remove"/>
<uses-permission android:name="android.permission.CAMERA" android:required="false" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-permission
android:name="android.permission.READ_PHONE_STATE"
tools:node="remove" />
<uses-permission
android:name="android.permission.CAMERA"
android:required="false" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />

<application
android:name=".MainApplication"
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:name=".MainApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="zulip" android:host="login" />
</intent-filter>
</activity>
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:label="@string/app_name"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="login"
android:scheme="zulip" />
</intent-filter>
</activity>

<activity
android:name=".sharing.ReceiveShareActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:label="@string/app_name"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<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
`com.zulipmobile.debug`, it then tries to start an activity
`com.zulipmobile.debug.MainActivity`. So provide that as an alias.
-->
<activity android:name=".debug.MainActivity" android:exported="true"/>
<!-- When `react-native run-android` learns from the decoy `package`
attribute in our comment above that the application ID is
`com.zulipmobile.debug`, it then tries to start an activity
`com.zulipmobile.debug.MainActivity`. So provide that as an alias.
-->
<activity
android:name=".debug.MainActivity"
android:exported="true" />

<service android:name=".notifications.NotificationIntentService" />
<service android:name=".notifications.NotificationIntentService" />

<service
android:name=".notifications.FcmListenerService"
android:exported="false" >
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service
android:name=".notifications.FcmListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>

<service
android:name=".notifications.InstanceIDListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
<service
android:name=".notifications.InstanceIDListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>

<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.zulipmobile.notifications.ConversationMap;
import com.zulipmobile.notifications.FCMPushNotifications;
import com.zulipmobile.notifications.NotificationsPackage;
import com.zulipmobile.sharing.SharingPackage;

public class MainApplication extends Application implements ReactApplication {
private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider(new BasePackageList().getPackageList(), null);
Expand Down Expand Up @@ -58,6 +59,7 @@ protected List<ReactPackage> getPackages() {
new RNDeviceInfo(),
new ZulipNativePackage(),
new NotificationsPackage(),
new SharingPackage(),
new ModuleRegistryAdapter(mModuleRegistryProvider)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ import com.zulipmobile.MainActivity
* getReactInstanceManager it'll try to create one... which asserts we're
* on the UI thread, which isn't true if e.g. we got here from a Service.
*/
private fun ReactNativeHost.tryGetReactInstanceManager(): ReactInstanceManager? =
fun ReactNativeHost.tryGetReactInstanceManager(): ReactInstanceManager? =
if (this.hasInstance()) this.reactInstanceManager else null

/**
* A distillation of ReactContext.getLifecycleState() and related information.
*
* See ReactContext.getAppStatus().
*/
private enum class ReactAppStatus {
enum class ReactAppStatus {
/**
* The main activity has either never yet been in the foreground,
* or never will again. There might not be an active JS instance.
Expand All @@ -55,7 +55,7 @@ private enum class ReactAppStatus {
FOREGROUND
}

private val ReactContext.appStatus: ReactAppStatus
val ReactContext.appStatus: ReactAppStatus
get() {
if (!hasActiveCatalystInstance())
return ReactAppStatus.NOT_RUNNING
Expand Down Expand Up @@ -98,13 +98,13 @@ internal fun notifyReact(application: ReactApplication, data: Bundle) {
}
}

internal fun emit(reactContext: ReactContext, eventName: String, data: Any?) {
fun emit(reactContext: ReactContext, eventName: String, data: Any?) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit(eventName, data)
}

private fun launchMainActivity(context: Context) {
fun launchMainActivity(context: Context) {
Log.d(TAG, "NotifyReact: launching main activity")
val intent = Intent(context, MainActivity::class.java)
// See these sections in the Android docs:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.zulipmobile.sharing

import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.webkit.WebView
import androidx.annotation.Nullable
import com.facebook.react.ReactActivity
import com.facebook.react.ReactApplication
import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.WritableMap
import com.facebook.react.bridge.Arguments
import com.zulipmobile.notifications.*

const val TAG = "ZulipReceiveShare"

class ReceiveShareActivity : ReactActivity() {

/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
override fun getMainComponentName(): String? {
return "SharingRoot"
}

private fun sendEvent(reactContext: ReactContext,
eventName: String,
@Nullable params: WritableMap) {
Log.d(TAG, "Sending event with shared data")
emit(reactContext, eventName, params)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WebView.setWebContentsDebuggingEnabled(true)
if (intent?.action == Intent.ACTION_SEND) {
handleSend(intent)
}
finish()
}

private fun handleSend(intent: Intent) {
val application = application as ReactApplication
val host = application.reactNativeHost
val reactContext = host.tryGetReactInstanceManager()?.currentReactContext

val params = (if ("text/plain" == intent.type) {
getParamsFromTextIntent(intent)
} else if (intent.type?.startsWith("image/") == true) {
getParamsFromImageIntent(intent)
} else {
getParamsFromFileIntent(intent)
})
?: throw Exception("Could not parse received sharing data")

if (null == reactContext) {
launchMainActivity(application as Context)
SharingModule.initialSharedData = params
} else {
sendEvent(reactContext, "shareReceived", params)
}
}

private fun getParamsFromTextIntent(intent: Intent): WritableMap {
val sharedText = intent.getStringExtra(Intent.EXTRA_TEXT)
val params = Arguments.createMap()
params.putString("type", "text")
params.putString("sharedText", sharedText)
return params
}

private fun getParamsFromImageIntent(intent: Intent): WritableMap? {
val uri = intent.getParcelableExtra(Intent.EXTRA_STREAM) as? Uri ?: return null
val params = Arguments.createMap()
params.putString("type", "image")
params.putString("sharedImageUri", uri.toString())
return params
}

private fun getParamsFromFileIntent(intent: Intent): WritableMap? {
val uri = intent.getParcelableExtra(Intent.EXTRA_STREAM) as? Uri ?: return null
val params = Arguments.createMap()
params.putString("type", "file")
params.putString("sharedFileUri", uri.toString())
return params
}

override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
val intent = Intent("onConfigurationChanged")
intent.putExtra("newConfig", newConfig)
this.sendBroadcast(intent)
}
}
24 changes: 24 additions & 0 deletions android/app/src/main/java/com/zulipmobile/sharing/SharingModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.zulipmobile.sharing

import com.facebook.react.bridge.*

internal class SharingModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {

override fun getName(): String {
return "Sharing"
}

@ReactMethod
fun getInitialSharedContent(promise: Promise) {
if (null == initialSharedData) {
promise.resolve(null)
} else {
promise.resolve(initialSharedData)
}

}

companion object {
var initialSharedData: WritableMap? = null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.zulipmobile.sharing


import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager

import java.util.ArrayList

class SharingPackage : ReactPackage {

override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList()
}

override fun createNativeModules(
reactContext: ReactApplicationContext): List<NativeModule> {
val modules = ArrayList<NativeModule>()

modules.add(SharingModule(reactContext))

return modules
}

}
2 changes: 2 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/* @flow strict-local */
import { AppRegistry } from 'react-native';
import ZulipMobile from './src/ZulipMobile';
import SharingRoot from './src/sharing/SharingRoot';

AppRegistry.registerComponent('ZulipMobile', () => ZulipMobile);
AppRegistry.registerComponent('SharingRoot', () => SharingRoot);
6 changes: 3 additions & 3 deletions src/api/messages/sendMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ export default async (
params: {|
type: 'private' | 'stream',
to: string,
subject: string,
subject?: string,
content: string,
localId: number,
eventQueueId: number,
localId?: number,
eventQueueId?: number,
|},
): Promise<ApiResponse> =>
apiPost(auth, 'messages', {
Expand Down
Loading