-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support react-native new architecture (#65)
* feat(android): support new architecture * feat(ios): support new architecture * feat(android): improve type conversation in releaseContext * fix: constants type defs * chore: docgen * fix(ios): requiresMainQueueSetup
- Loading branch information
Showing
16 changed files
with
779 additions
and
104 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 33 additions & 13 deletions
46
android/src/main/java/com/rnwhisper/RNWhisperPackage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,48 @@ | ||
package com.rnwhisper; | ||
|
||
import androidx.annotation.NonNull; | ||
import androidx.annotation.Nullable; | ||
|
||
import com.facebook.react.ReactPackage; | ||
import com.facebook.react.bridge.NativeModule; | ||
import com.facebook.react.bridge.ReactApplicationContext; | ||
import com.facebook.react.uimanager.ViewManager; | ||
import com.facebook.react.module.model.ReactModuleInfo; | ||
import com.facebook.react.module.model.ReactModuleInfoProvider; | ||
import com.facebook.react.TurboReactPackage; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
public class RNWhisperPackage implements ReactPackage { | ||
@NonNull | ||
public class RNWhisperPackage extends TurboReactPackage { | ||
|
||
@Nullable | ||
@Override | ||
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) { | ||
List<NativeModule> modules = new ArrayList<>(); | ||
modules.add(new RNWhisperModule(reactContext)); | ||
return modules; | ||
public NativeModule getModule(String name, ReactApplicationContext reactContext) { | ||
if (name.equals(RNWhisperModule.NAME)) { | ||
return new com.rnwhisper.RNWhisperModule(reactContext); | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
@NonNull | ||
@Override | ||
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) { | ||
return Collections.emptyList(); | ||
public ReactModuleInfoProvider getReactModuleInfoProvider() { | ||
return () -> { | ||
final Map<String, ReactModuleInfo> moduleInfos = new HashMap<>(); | ||
boolean isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; | ||
moduleInfos.put( | ||
RNWhisperModule.NAME, | ||
new ReactModuleInfo( | ||
RNWhisperModule.NAME, | ||
RNWhisperModule.NAME, | ||
false, // canOverrideExistingModule | ||
false, // needsEagerInit | ||
true, // hasConstants | ||
false, // isCxxModule | ||
isTurboModule // isTurboModule | ||
) | ||
); | ||
return moduleInfos; | ||
}; | ||
} | ||
} |
232 changes: 232 additions & 0 deletions
232
android/src/newarch/java/com/rnwhisper/RNWhisperModule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
package com.rnwhisper; | ||
|
||
import androidx.annotation.NonNull; | ||
import android.util.Log; | ||
import android.os.Build; | ||
import android.os.Handler; | ||
import android.os.AsyncTask; | ||
import android.media.AudioRecord; | ||
|
||
import com.facebook.react.bridge.Promise; | ||
import com.facebook.react.bridge.ReactApplicationContext; | ||
import com.facebook.react.bridge.ReactMethod; | ||
import com.facebook.react.bridge.LifecycleEventListener; | ||
import com.facebook.react.bridge.ReadableMap; | ||
import com.facebook.react.bridge.WritableMap; | ||
import com.facebook.react.module.annotations.ReactModule; | ||
|
||
import java.util.HashMap; | ||
import java.util.Random; | ||
|
||
@ReactModule(name = RNWhisperModule.NAME) | ||
public class RNWhisperModule extends NativeRNWhisperSpec implements LifecycleEventListener { | ||
public static final String NAME = "RNWhisper"; | ||
|
||
private ReactApplicationContext reactContext; | ||
|
||
public RNWhisperModule(ReactApplicationContext reactContext) { | ||
super(reactContext); | ||
reactContext.addLifecycleEventListener(this); | ||
this.reactContext = reactContext; | ||
} | ||
|
||
@Override | ||
@NonNull | ||
public String getName() { | ||
return NAME; | ||
} | ||
|
||
@Override | ||
public HashMap<String, Object> getTypedExportedConstants() { | ||
HashMap<String, Object> constants = new HashMap<>(); | ||
|
||
// iOS only constants, put for passing type checks | ||
constants.put("useCoreML", false); | ||
constants.put("coreMLAllowFallback", false); | ||
|
||
return constants; | ||
} | ||
|
||
private HashMap<Integer, WhisperContext> contexts = new HashMap<>(); | ||
|
||
@ReactMethod | ||
public void initContext(final String modelPath, final boolean isBundleAsset, final Promise promise) { | ||
new AsyncTask<Void, Void, Integer>() { | ||
private Exception exception; | ||
|
||
@Override | ||
protected Integer doInBackground(Void... voids) { | ||
try { | ||
long context; | ||
if (isBundleAsset) { | ||
context = WhisperContext.initContextWithAsset(reactContext.getAssets(), modelPath); | ||
} else { | ||
context = WhisperContext.initContext(modelPath); | ||
} | ||
if (context == 0) { | ||
throw new Exception("Failed to initialize context"); | ||
} | ||
int id = Math.abs(new Random().nextInt()); | ||
WhisperContext whisperContext = new WhisperContext(id, reactContext, context); | ||
contexts.put(id, whisperContext); | ||
return id; | ||
} catch (Exception e) { | ||
exception = e; | ||
return null; | ||
} | ||
} | ||
|
||
@Override | ||
protected void onPostExecute(Integer id) { | ||
if (exception != null) { | ||
promise.reject(exception); | ||
return; | ||
} | ||
promise.resolve(id); | ||
} | ||
}.execute(); | ||
} | ||
|
||
@ReactMethod | ||
public void transcribeFile(double id, double jobId, String filePath, ReadableMap options, Promise promise) { | ||
final WhisperContext context = contexts.get((int) id); | ||
if (context == null) { | ||
promise.reject("Context not found"); | ||
return; | ||
} | ||
if (context.isCapturing()) { | ||
promise.reject("The context is in realtime transcribe mode"); | ||
return; | ||
} | ||
if (context.isTranscribing()) { | ||
promise.reject("Context is already transcribing"); | ||
return; | ||
} | ||
new AsyncTask<Void, Void, WritableMap>() { | ||
private Exception exception; | ||
|
||
@Override | ||
protected WritableMap doInBackground(Void... voids) { | ||
try { | ||
return context.transcribeFile((int) jobId, filePath, options); | ||
} catch (Exception e) { | ||
exception = e; | ||
return null; | ||
} | ||
} | ||
|
||
@Override | ||
protected void onPostExecute(WritableMap data) { | ||
if (exception != null) { | ||
promise.reject(exception); | ||
return; | ||
} | ||
promise.resolve(data); | ||
} | ||
}.execute(); | ||
} | ||
|
||
@ReactMethod | ||
public void startRealtimeTranscribe(double id, double jobId, ReadableMap options, Promise promise) { | ||
final WhisperContext context = contexts.get((int) id); | ||
if (context == null) { | ||
promise.reject("Context not found"); | ||
return; | ||
} | ||
if (context.isCapturing()) { | ||
promise.reject("Context is already in capturing"); | ||
return; | ||
} | ||
int state = context.startRealtimeTranscribe((int) jobId, options); | ||
if (state == AudioRecord.STATE_INITIALIZED) { | ||
promise.resolve(null); | ||
return; | ||
} | ||
promise.reject("Failed to start realtime transcribe. State: " + state); | ||
} | ||
|
||
@ReactMethod | ||
public void abortTranscribe(double contextId, double jobId, Promise promise) { | ||
WhisperContext context = contexts.get((int) contextId); | ||
if (context == null) { | ||
promise.reject("Context not found"); | ||
return; | ||
} | ||
context.stopTranscribe((int) jobId); | ||
} | ||
|
||
@ReactMethod | ||
public void releaseContext(double id, Promise promise) { | ||
final int contextId = (int) id; | ||
new AsyncTask<Void, Void, Void>() { | ||
private Exception exception; | ||
|
||
@Override | ||
protected Void doInBackground(Void... voids) { | ||
try { | ||
WhisperContext context = contexts.get(contextId); | ||
if (context == null) { | ||
throw new Exception("Context " + id + " not found"); | ||
} | ||
context.release(); | ||
contexts.remove(contextId); | ||
} catch (Exception e) { | ||
exception = e; | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
protected void onPostExecute(Void result) { | ||
if (exception != null) { | ||
promise.reject(exception); | ||
return; | ||
} | ||
promise.resolve(null); | ||
} | ||
}.execute(); | ||
} | ||
|
||
@ReactMethod | ||
public void releaseAllContexts(Promise promise) { | ||
new AsyncTask<Void, Void, Void>() { | ||
private Exception exception; | ||
|
||
@Override | ||
protected Void doInBackground(Void... voids) { | ||
try { | ||
onHostDestroy(); | ||
} catch (Exception e) { | ||
exception = e; | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
protected void onPostExecute(Void result) { | ||
if (exception != null) { | ||
promise.reject(exception); | ||
return; | ||
} | ||
promise.resolve(null); | ||
} | ||
}.execute(); | ||
} | ||
|
||
@Override | ||
public void onHostResume() { | ||
} | ||
|
||
@Override | ||
public void onHostPause() { | ||
} | ||
|
||
@Override | ||
public void onHostDestroy() { | ||
WhisperContext.abortAllTranscribe(); | ||
for (WhisperContext context : contexts.values()) { | ||
context.release(); | ||
} | ||
contexts.clear(); | ||
} | ||
} |
File renamed without changes.
Oops, something went wrong.