0) {
280 | println "android.packagingOptions.$prop += $options ($options.length)"
281 | // Ex: android.packagingOptions.pickFirsts += '**/SCCS/**'
282 | options.each {
283 | android.packagingOptions[prop] += it
284 | }
285 | }
286 | }
287 |
288 | dependencies {
289 | implementation fileTree(dir: "libs", include: ["*.jar"])
290 |
291 | //noinspection GradleDynamicVersion
292 | implementation "com.facebook.react:react-native:+" // From node_modules
293 |
294 | def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
295 | def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true";
296 | def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true";
297 | def frescoVersion = rootProject.ext.frescoVersion
298 |
299 | // If your app supports Android versions before Ice Cream Sandwich (API level 14)
300 | if (isGifEnabled || isWebpEnabled) {
301 | implementation "com.facebook.fresco:fresco:${frescoVersion}"
302 | implementation "com.facebook.fresco:imagepipeline-okhttp3:${frescoVersion}"
303 | }
304 |
305 | if (isGifEnabled) {
306 | // For animated gif support
307 | implementation "com.facebook.fresco:animated-gif:${frescoVersion}"
308 | }
309 |
310 | if (isWebpEnabled) {
311 | // For webp support
312 | implementation "com.facebook.fresco:webpsupport:${frescoVersion}"
313 | if (isWebpAnimatedEnabled) {
314 | // Animated webp support
315 | implementation "com.facebook.fresco:animated-webp:${frescoVersion}"
316 | }
317 | }
318 |
319 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
320 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
321 | exclude group:'com.facebook.fbjni'
322 | }
323 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
324 | exclude group:'com.facebook.flipper'
325 | exclude group:'com.squareup.okhttp3', module:'okhttp'
326 | }
327 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
328 | exclude group:'com.facebook.flipper'
329 | }
330 |
331 | if (enableHermes) {
332 | debugImplementation files(new File(["node", "--print", "require.resolve('hermes-engine/package.json')"].execute(null, rootDir).text.trim(), "../android/hermes-debug.aar"))
333 | releaseImplementation files(new File(["node", "--print", "require.resolve('hermes-engine/package.json')"].execute(null, rootDir).text.trim(), "../android/hermes-release.aar"))
334 | } else {
335 | implementation jscFlavor
336 | }
337 | }
338 |
339 | if (isNewArchitectureEnabled()) {
340 | // If new architecture is enabled, we let you build RN from source
341 | // Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
342 | // This will be applied to all the imported transtitive dependency.
343 | configurations.all {
344 | resolutionStrategy.dependencySubstitution {
345 | substitute(module("com.facebook.react:react-native"))
346 | .using(project(":ReactAndroid")).because("On New Architecture we're building React Native from source")
347 | }
348 | }
349 | }
350 |
351 | // Run this once to be able to run the application with BUCK
352 | // puts all compile dependencies into folder libs for BUCK to use
353 | task copyDownloadableDepsToLibs(type: Copy) {
354 | from configurations.implementation
355 | into 'libs'
356 | }
357 |
358 | apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
359 | applyNativeModulesAppBuildGradle(project)
360 |
361 | def isNewArchitectureEnabled() {
362 | // To opt-in for the New Architecture, you can either:
363 | // - Set `newArchEnabled` to true inside the `gradle.properties` file
364 | // - Invoke gradle with `-newArchEnabled=true`
365 | // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
366 | return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
367 | }
368 |
--------------------------------------------------------------------------------
/example/android/app/build_defs.bzl:
--------------------------------------------------------------------------------
1 | """Helper definitions to glob .aar and .jar targets"""
2 |
3 | def create_aar_targets(aarfiles):
4 | for aarfile in aarfiles:
5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
6 | lib_deps.append(":" + name)
7 | android_prebuilt_aar(
8 | name = name,
9 | aar = aarfile,
10 | )
11 |
12 | def create_jar_targets(jarfiles):
13 | for jarfile in jarfiles:
14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
15 | lib_deps.append(":" + name)
16 | prebuilt_jar(
17 | name = name,
18 | binary_jar = jarfile,
19 | )
20 |
--------------------------------------------------------------------------------
/example/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/debug.keystore
--------------------------------------------------------------------------------
/example/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
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 | # react-native-reanimated
11 | -keep class com.swmansion.reanimated.** { *; }
12 | -keep class com.facebook.react.turbomodule.** { *; }
13 |
14 | # Add any project specific keep options here:
15 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/java/expo/community/modules/musicpicker/example/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package expo.community.modules.musicpicker.example;
8 |
9 | import android.content.Context;
10 | import com.facebook.flipper.android.AndroidFlipperClient;
11 | import com.facebook.flipper.android.utils.FlipperUtils;
12 | import com.facebook.flipper.core.FlipperClient;
13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping;
17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
22 | import com.facebook.react.ReactInstanceManager;
23 | import com.facebook.react.bridge.ReactContext;
24 | import com.facebook.react.modules.network.NetworkingModule;
25 | import okhttp3.OkHttpClient;
26 |
27 | public class ReactNativeFlipper {
28 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
29 | if (FlipperUtils.shouldEnableFlipper(context)) {
30 | final FlipperClient client = AndroidFlipperClient.getInstance(context);
31 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
32 | client.addPlugin(new ReactFlipperPlugin());
33 | client.addPlugin(new DatabasesFlipperPlugin(context));
34 | client.addPlugin(new SharedPreferencesFlipperPlugin(context));
35 | client.addPlugin(CrashReporterPlugin.getInstance());
36 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
37 | NetworkingModule.setCustomClientBuilder(
38 | new NetworkingModule.CustomClientBuilder() {
39 | @Override
40 | public void apply(OkHttpClient.Builder builder) {
41 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
42 | }
43 | });
44 | client.addPlugin(networkFlipperPlugin);
45 | client.start();
46 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
47 | // Hence we run if after all native modules have been initialized
48 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
49 | if (reactContext == null) {
50 | reactInstanceManager.addReactInstanceEventListener(
51 | new ReactInstanceManager.ReactInstanceEventListener() {
52 | @Override
53 | public void onReactContextInitialized(ReactContext reactContext) {
54 | reactInstanceManager.removeReactInstanceEventListener(this);
55 | reactContext.runOnNativeModulesQueueThread(
56 | new Runnable() {
57 | @Override
58 | public void run() {
59 | client.addPlugin(new FrescoFlipperPlugin());
60 | }
61 | });
62 | }
63 | });
64 | } else {
65 | client.addPlugin(new FrescoFlipperPlugin());
66 | }
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/expo/community/modules/musicpicker/example/MainActivity.java:
--------------------------------------------------------------------------------
1 | package expo.community.modules.musicpicker.example;
2 |
3 | import android.os.Build;
4 | import android.os.Bundle;
5 |
6 | import com.facebook.react.ReactActivity;
7 | import com.facebook.react.ReactActivityDelegate;
8 | import com.facebook.react.ReactRootView;
9 |
10 | import expo.modules.ReactActivityDelegateWrapper;
11 |
12 | public class MainActivity extends ReactActivity {
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | // Set the theme to AppTheme BEFORE onCreate to support
16 | // coloring the background, status bar, and navigation bar.
17 | // This is required for expo-splash-screen.
18 | setTheme(R.style.AppTheme);
19 | super.onCreate(null);
20 | }
21 |
22 | /**
23 | * Returns the name of the main component registered from JavaScript.
24 | * This is used to schedule rendering of the component.
25 | */
26 | @Override
27 | protected String getMainComponentName() {
28 | return "main";
29 | }
30 |
31 | @Override
32 | protected ReactActivityDelegate createReactActivityDelegate() {
33 | return new ReactActivityDelegateWrapper(this,
34 | new ReactActivityDelegate(this, getMainComponentName())
35 | );
36 | }
37 |
38 | /**
39 | * Align the back button behavior with Android S
40 | * where moving root activities to background instead of finishing activities.
41 | * @see onBackPressed
42 | */
43 | @Override
44 | public void invokeDefaultOnBackPressed() {
45 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
46 | if (!moveTaskToBack(false)) {
47 | // For non-root activities, use the default implementation to finish them.
48 | super.invokeDefaultOnBackPressed();
49 | }
50 | return;
51 | }
52 |
53 | // Use the default back button implementation on Android S
54 | // because it's doing more than {@link Activity#moveTaskToBack} in fact.
55 | super.invokeDefaultOnBackPressed();
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/expo/community/modules/musicpicker/example/MainApplication.java:
--------------------------------------------------------------------------------
1 | package expo.community.modules.musicpicker.example;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import android.content.res.Configuration;
6 | import androidx.annotation.NonNull;
7 |
8 | import com.facebook.react.PackageList;
9 | import com.facebook.react.ReactApplication;
10 | import com.facebook.react.ReactInstanceManager;
11 | import com.facebook.react.ReactNativeHost;
12 | import com.facebook.react.ReactPackage;
13 | import com.facebook.react.config.ReactFeatureFlags;
14 | import com.facebook.soloader.SoLoader;
15 | import expo.community.modules.musicpicker.example.newarchitecture.MainApplicationReactNativeHost;
16 |
17 | import expo.modules.ApplicationLifecycleDispatcher;
18 | import expo.modules.ReactNativeHostWrapper;
19 |
20 | import java.lang.reflect.InvocationTargetException;
21 | import java.util.List;
22 |
23 | public class MainApplication extends Application implements ReactApplication {
24 | private final ReactNativeHost mReactNativeHost = new ReactNativeHostWrapper(
25 | this,
26 | new ReactNativeHost(this) {
27 | @Override
28 | public boolean getUseDeveloperSupport() {
29 | return BuildConfig.DEBUG;
30 | }
31 |
32 | @Override
33 | protected List getPackages() {
34 | @SuppressWarnings("UnnecessaryLocalVariable")
35 | List packages = new PackageList(this).getPackages();
36 | // Packages that cannot be autolinked yet can be added manually here, for example:
37 | // packages.add(new MyReactNativePackage());
38 | return packages;
39 | }
40 |
41 | @Override
42 | protected String getJSMainModuleName() {
43 | return "index";
44 | }
45 | });
46 |
47 | private final ReactNativeHost mNewArchitectureNativeHost =
48 | new ReactNativeHostWrapper(this, new MainApplicationReactNativeHost(this));
49 |
50 | @Override
51 | public ReactNativeHost getReactNativeHost() {
52 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
53 | return mNewArchitectureNativeHost;
54 | } else {
55 | return mReactNativeHost;
56 | }
57 | }
58 |
59 | @Override
60 | public void onCreate() {
61 | super.onCreate();
62 | // If you opted-in for the New Architecture, we enable the TurboModule system
63 | ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
64 | SoLoader.init(this, /* native exopackage */ false);
65 |
66 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
67 | ApplicationLifecycleDispatcher.onApplicationCreate(this);
68 | }
69 |
70 | @Override
71 | public void onConfigurationChanged(@NonNull Configuration newConfig) {
72 | super.onConfigurationChanged(newConfig);
73 | ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig);
74 | }
75 |
76 | /**
77 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like
78 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
79 | *
80 | * @param context
81 | * @param reactInstanceManager
82 | */
83 | private static void initializeFlipper(
84 | Context context, ReactInstanceManager reactInstanceManager) {
85 | if (BuildConfig.DEBUG) {
86 | try {
87 | /*
88 | We use reflection here to pick up the class that initializes Flipper,
89 | since Flipper library is not available in release mode
90 | */
91 | Class> aClass = Class.forName("expo.community.modules.musicpicker.example.ReactNativeFlipper");
92 | aClass
93 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
94 | .invoke(null, context, reactInstanceManager);
95 | } catch (ClassNotFoundException e) {
96 | e.printStackTrace();
97 | } catch (NoSuchMethodException e) {
98 | e.printStackTrace();
99 | } catch (IllegalAccessException e) {
100 | e.printStackTrace();
101 | } catch (InvocationTargetException e) {
102 | e.printStackTrace();
103 | }
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/expo/community/modules/musicpicker/example/newarchitecture/MainApplicationReactNativeHost.java:
--------------------------------------------------------------------------------
1 | package expo.community.modules.musicpicker.example.newarchitecture;
2 |
3 | import android.app.Application;
4 | import androidx.annotation.NonNull;
5 | import com.facebook.react.PackageList;
6 | import com.facebook.react.ReactInstanceManager;
7 | import com.facebook.react.ReactNativeHost;
8 | import com.facebook.react.ReactPackage;
9 | import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
10 | import com.facebook.react.bridge.JSIModulePackage;
11 | import com.facebook.react.bridge.JSIModuleProvider;
12 | import com.facebook.react.bridge.JSIModuleSpec;
13 | import com.facebook.react.bridge.JSIModuleType;
14 | import com.facebook.react.bridge.JavaScriptContextHolder;
15 | import com.facebook.react.bridge.ReactApplicationContext;
16 | import com.facebook.react.bridge.UIManager;
17 | import com.facebook.react.fabric.ComponentFactory;
18 | import com.facebook.react.fabric.CoreComponentsRegistry;
19 | import com.facebook.react.fabric.EmptyReactNativeConfig;
20 | import com.facebook.react.fabric.FabricJSIModuleProvider;
21 | import com.facebook.react.uimanager.ViewManagerRegistry;
22 | import expo.community.modules.musicpicker.example.BuildConfig;
23 | import expo.community.modules.musicpicker.example.newarchitecture.components.MainComponentsRegistry;
24 | import expo.community.modules.musicpicker.example.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
25 | import java.util.ArrayList;
26 | import java.util.List;
27 |
28 | /**
29 | * A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both
30 | * TurboModule delegates and the Fabric Renderer.
31 | *
32 | * Please note that this class is used ONLY if you opt-in for the New Architecture (see the
33 | * `newArchEnabled` property). Is ignored otherwise.
34 | */
35 | public class MainApplicationReactNativeHost extends ReactNativeHost {
36 | public MainApplicationReactNativeHost(Application application) {
37 | super(application);
38 | }
39 |
40 | @Override
41 | public boolean getUseDeveloperSupport() {
42 | return BuildConfig.DEBUG;
43 | }
44 |
45 | @Override
46 | protected List getPackages() {
47 | List packages = new PackageList(this).getPackages();
48 | // Packages that cannot be autolinked yet can be added manually here, for example:
49 | // packages.add(new MyReactNativePackage());
50 | // TurboModules must also be loaded here providing a valid TurboReactPackage implementation:
51 | // packages.add(new TurboReactPackage() { ... });
52 | // If you have custom Fabric Components, their ViewManagers should also be loaded here
53 | // inside a ReactPackage.
54 | return packages;
55 | }
56 |
57 | @Override
58 | protected String getJSMainModuleName() {
59 | return "index";
60 | }
61 |
62 | @NonNull
63 | @Override
64 | protected ReactPackageTurboModuleManagerDelegate.Builder
65 | getReactPackageTurboModuleManagerDelegateBuilder() {
66 | // Here we provide the ReactPackageTurboModuleManagerDelegate Builder. This is necessary
67 | // for the new architecture and to use TurboModules correctly.
68 | return new MainApplicationTurboModuleManagerDelegate.Builder();
69 | }
70 |
71 | @Override
72 | protected JSIModulePackage getJSIModulePackage() {
73 | return new JSIModulePackage() {
74 | @Override
75 | public List getJSIModules(
76 | final ReactApplicationContext reactApplicationContext,
77 | final JavaScriptContextHolder jsContext) {
78 | final List specs = new ArrayList<>();
79 |
80 | // Here we provide a new JSIModuleSpec that will be responsible of providing the
81 | // custom Fabric Components.
82 | specs.add(
83 | new JSIModuleSpec() {
84 | @Override
85 | public JSIModuleType getJSIModuleType() {
86 | return JSIModuleType.UIManager;
87 | }
88 |
89 | @Override
90 | public JSIModuleProvider getJSIModuleProvider() {
91 | final ComponentFactory componentFactory = new ComponentFactory();
92 | CoreComponentsRegistry.register(componentFactory);
93 |
94 | // Here we register a Components Registry.
95 | // The one that is generated with the template contains no components
96 | // and just provides you the one from React Native core.
97 | MainComponentsRegistry.register(componentFactory);
98 |
99 | final ReactInstanceManager reactInstanceManager = getReactInstanceManager();
100 |
101 | ViewManagerRegistry viewManagerRegistry =
102 | new ViewManagerRegistry(
103 | reactInstanceManager.getOrCreateViewManagers(reactApplicationContext));
104 |
105 | return new FabricJSIModuleProvider(
106 | reactApplicationContext,
107 | componentFactory,
108 | new EmptyReactNativeConfig(),
109 | viewManagerRegistry);
110 | }
111 | });
112 | return specs;
113 | }
114 | };
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/expo/community/modules/musicpicker/example/newarchitecture/components/MainComponentsRegistry.java:
--------------------------------------------------------------------------------
1 | package expo.community.modules.musicpicker.example.newarchitecture.components;
2 |
3 | import com.facebook.jni.HybridData;
4 | import com.facebook.proguard.annotations.DoNotStrip;
5 | import com.facebook.react.fabric.ComponentFactory;
6 | import com.facebook.soloader.SoLoader;
7 |
8 | /**
9 | * Class responsible to load the custom Fabric Components. This class has native methods and needs a
10 | * corresponding C++ implementation/header file to work correctly (already placed inside the jni/
11 | * folder for you).
12 | *
13 | * Please note that this class is used ONLY if you opt-in for the New Architecture (see the
14 | * `newArchEnabled` property). Is ignored otherwise.
15 | */
16 | @DoNotStrip
17 | public class MainComponentsRegistry {
18 | static {
19 | SoLoader.loadLibrary("fabricjni");
20 | }
21 |
22 | @DoNotStrip private final HybridData mHybridData;
23 |
24 | @DoNotStrip
25 | private native HybridData initHybrid(ComponentFactory componentFactory);
26 |
27 | @DoNotStrip
28 | private MainComponentsRegistry(ComponentFactory componentFactory) {
29 | mHybridData = initHybrid(componentFactory);
30 | }
31 |
32 | @DoNotStrip
33 | public static MainComponentsRegistry register(ComponentFactory componentFactory) {
34 | return new MainComponentsRegistry(componentFactory);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/expo/community/modules/musicpicker/example/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java:
--------------------------------------------------------------------------------
1 | package expo.community.modules.musicpicker.example.newarchitecture.modules;
2 |
3 | import com.facebook.jni.HybridData;
4 | import com.facebook.react.ReactPackage;
5 | import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.soloader.SoLoader;
8 | import java.util.List;
9 |
10 | /**
11 | * Class responsible to load the TurboModules. This class has native methods and needs a
12 | * corresponding C++ implementation/header file to work correctly (already placed inside the jni/
13 | * folder for you).
14 | *
15 | *
Please note that this class is used ONLY if you opt-in for the New Architecture (see the
16 | * `newArchEnabled` property). Is ignored otherwise.
17 | */
18 | public class MainApplicationTurboModuleManagerDelegate
19 | extends ReactPackageTurboModuleManagerDelegate {
20 |
21 | private static volatile boolean sIsSoLibraryLoaded;
22 |
23 | protected MainApplicationTurboModuleManagerDelegate(
24 | ReactApplicationContext reactApplicationContext, List packages) {
25 | super(reactApplicationContext, packages);
26 | }
27 |
28 | protected native HybridData initHybrid();
29 |
30 | native boolean canCreateTurboModule(String moduleName);
31 |
32 | public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder {
33 | protected MainApplicationTurboModuleManagerDelegate build(
34 | ReactApplicationContext context, List packages) {
35 | return new MainApplicationTurboModuleManagerDelegate(context, packages);
36 | }
37 | }
38 |
39 | @Override
40 | protected synchronized void maybeLoadOtherSoLibraries() {
41 | if (!sIsSoLibraryLoaded) {
42 | // If you change the name of your application .so file in the Android.mk file,
43 | // make sure you update the name here as well.
44 | SoLoader.loadLibrary("expomusicpickerexample_appmodules");
45 | sIsSoLibraryLoaded = true;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/example/android/app/src/main/jni/Android.mk:
--------------------------------------------------------------------------------
1 | THIS_DIR := $(call my-dir)
2 |
3 | include $(REACT_ANDROID_DIR)/Android-prebuilt.mk
4 |
5 | # If you wish to add a custom TurboModule or Fabric component in your app you
6 | # will have to include the following autogenerated makefile.
7 | # include $(GENERATED_SRC_DIR)/codegen/jni/Android.mk
8 | include $(CLEAR_VARS)
9 |
10 | LOCAL_PATH := $(THIS_DIR)
11 |
12 | # You can customize the name of your application .so file here.
13 | LOCAL_MODULE := expomusicpickerexample_appmodules
14 |
15 | LOCAL_C_INCLUDES := $(LOCAL_PATH)
16 | LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
17 | LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
18 |
19 | # If you wish to add a custom TurboModule or Fabric component in your app you
20 | # will have to uncomment those lines to include the generated source
21 | # files from the codegen (placed in $(GENERATED_SRC_DIR)/codegen/jni)
22 | #
23 | # LOCAL_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni
24 | # LOCAL_SRC_FILES += $(wildcard $(GENERATED_SRC_DIR)/codegen/jni/*.cpp)
25 | # LOCAL_EXPORT_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni
26 |
27 | # Here you should add any native library you wish to depend on.
28 | LOCAL_SHARED_LIBRARIES := \
29 | libfabricjni \
30 | libfbjni \
31 | libfolly_futures \
32 | libfolly_json \
33 | libglog \
34 | libjsi \
35 | libreact_codegen_rncore \
36 | libreact_debug \
37 | libreact_nativemodule_core \
38 | libreact_render_componentregistry \
39 | libreact_render_core \
40 | libreact_render_debug \
41 | libreact_render_graphics \
42 | librrc_view \
43 | libruntimeexecutor \
44 | libturbomodulejsijni \
45 | libyoga
46 |
47 | LOCAL_CFLAGS := -DLOG_TAG=\"ReactNative\" -fexceptions -frtti -std=c++17 -Wall
48 |
49 | include $(BUILD_SHARED_LIBRARY)
50 |
--------------------------------------------------------------------------------
/example/android/app/src/main/jni/MainApplicationModuleProvider.cpp:
--------------------------------------------------------------------------------
1 | #include "MainApplicationModuleProvider.h"
2 |
3 | #include
4 |
5 | namespace facebook {
6 | namespace react {
7 |
8 | std::shared_ptr MainApplicationModuleProvider(
9 | const std::string moduleName,
10 | const JavaTurboModule::InitParams ¶ms) {
11 | // Here you can provide your own module provider for TurboModules coming from
12 | // either your application or from external libraries. The approach to follow
13 | // is similar to the following (for a library called `samplelibrary`:
14 | //
15 | // auto module = samplelibrary_ModuleProvider(moduleName, params);
16 | // if (module != nullptr) {
17 | // return module;
18 | // }
19 | // return rncore_ModuleProvider(moduleName, params);
20 | return rncore_ModuleProvider(moduleName, params);
21 | }
22 |
23 | } // namespace react
24 | } // namespace facebook
25 |
--------------------------------------------------------------------------------
/example/android/app/src/main/jni/MainApplicationModuleProvider.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include
7 |
8 | namespace facebook {
9 | namespace react {
10 |
11 | std::shared_ptr MainApplicationModuleProvider(
12 | const std::string moduleName,
13 | const JavaTurboModule::InitParams ¶ms);
14 |
15 | } // namespace react
16 | } // namespace facebook
17 |
--------------------------------------------------------------------------------
/example/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.cpp:
--------------------------------------------------------------------------------
1 | #include "MainApplicationTurboModuleManagerDelegate.h"
2 | #include "MainApplicationModuleProvider.h"
3 |
4 | namespace facebook {
5 | namespace react {
6 |
7 | jni::local_ref
8 | MainApplicationTurboModuleManagerDelegate::initHybrid(
9 | jni::alias_ref) {
10 | return makeCxxInstance();
11 | }
12 |
13 | void MainApplicationTurboModuleManagerDelegate::registerNatives() {
14 | registerHybrid({
15 | makeNativeMethod(
16 | "initHybrid", MainApplicationTurboModuleManagerDelegate::initHybrid),
17 | makeNativeMethod(
18 | "canCreateTurboModule",
19 | MainApplicationTurboModuleManagerDelegate::canCreateTurboModule),
20 | });
21 | }
22 |
23 | std::shared_ptr
24 | MainApplicationTurboModuleManagerDelegate::getTurboModule(
25 | const std::string name,
26 | const std::shared_ptr jsInvoker) {
27 | // Not implemented yet: provide pure-C++ NativeModules here.
28 | return nullptr;
29 | }
30 |
31 | std::shared_ptr
32 | MainApplicationTurboModuleManagerDelegate::getTurboModule(
33 | const std::string name,
34 | const JavaTurboModule::InitParams ¶ms) {
35 | return MainApplicationModuleProvider(name, params);
36 | }
37 |
38 | bool MainApplicationTurboModuleManagerDelegate::canCreateTurboModule(
39 | std::string name) {
40 | return getTurboModule(name, nullptr) != nullptr ||
41 | getTurboModule(name, {.moduleName = name}) != nullptr;
42 | }
43 |
44 | } // namespace react
45 | } // namespace facebook
46 |
--------------------------------------------------------------------------------
/example/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include
5 | #include
6 |
7 | namespace facebook {
8 | namespace react {
9 |
10 | class MainApplicationTurboModuleManagerDelegate
11 | : public jni::HybridClass<
12 | MainApplicationTurboModuleManagerDelegate,
13 | TurboModuleManagerDelegate> {
14 | public:
15 | // Adapt it to the package you used for your Java class.
16 | static constexpr auto kJavaDescriptor =
17 | "Lexpo/community/modules/musicpicker/example/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate;";
18 |
19 | static jni::local_ref initHybrid(jni::alias_ref);
20 |
21 | static void registerNatives();
22 |
23 | std::shared_ptr getTurboModule(
24 | const std::string name,
25 | const std::shared_ptr jsInvoker) override;
26 | std::shared_ptr getTurboModule(
27 | const std::string name,
28 | const JavaTurboModule::InitParams ¶ms) override;
29 |
30 | /**
31 | * Test-only method. Allows user to verify whether a TurboModule can be
32 | * created by instances of this class.
33 | */
34 | bool canCreateTurboModule(std::string name);
35 | };
36 |
37 | } // namespace react
38 | } // namespace facebook
39 |
--------------------------------------------------------------------------------
/example/android/app/src/main/jni/MainComponentsRegistry.cpp:
--------------------------------------------------------------------------------
1 | #include "MainComponentsRegistry.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | namespace facebook {
9 | namespace react {
10 |
11 | MainComponentsRegistry::MainComponentsRegistry(ComponentFactory *delegate) {}
12 |
13 | std::shared_ptr
14 | MainComponentsRegistry::sharedProviderRegistry() {
15 | auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();
16 |
17 | // Custom Fabric Components go here. You can register custom
18 | // components coming from your App or from 3rd party libraries here.
19 | //
20 | // providerRegistry->add(concreteComponentDescriptorProvider<
21 | // AocViewerComponentDescriptor>());
22 | return providerRegistry;
23 | }
24 |
25 | jni::local_ref
26 | MainComponentsRegistry::initHybrid(
27 | jni::alias_ref,
28 | ComponentFactory *delegate) {
29 | auto instance = makeCxxInstance(delegate);
30 |
31 | auto buildRegistryFunction =
32 | [](EventDispatcher::Weak const &eventDispatcher,
33 | ContextContainer::Shared const &contextContainer)
34 | -> ComponentDescriptorRegistry::Shared {
35 | auto registry = MainComponentsRegistry::sharedProviderRegistry()
36 | ->createComponentDescriptorRegistry(
37 | {eventDispatcher, contextContainer});
38 |
39 | auto mutableRegistry =
40 | std::const_pointer_cast(registry);
41 |
42 | mutableRegistry->setFallbackComponentDescriptor(
43 | std::make_shared(
44 | ComponentDescriptorParameters{
45 | eventDispatcher, contextContainer, nullptr}));
46 |
47 | return registry;
48 | };
49 |
50 | delegate->buildRegistryFunction = buildRegistryFunction;
51 | return instance;
52 | }
53 |
54 | void MainComponentsRegistry::registerNatives() {
55 | registerHybrid({
56 | makeNativeMethod("initHybrid", MainComponentsRegistry::initHybrid),
57 | });
58 | }
59 |
60 | } // namespace react
61 | } // namespace facebook
62 |
--------------------------------------------------------------------------------
/example/android/app/src/main/jni/MainComponentsRegistry.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | namespace facebook {
9 | namespace react {
10 |
11 | class MainComponentsRegistry
12 | : public facebook::jni::HybridClass {
13 | public:
14 | // Adapt it to the package you used for your Java class.
15 | constexpr static auto kJavaDescriptor =
16 | "Lexpo/community/modules/musicpicker/example/newarchitecture/components/MainComponentsRegistry;";
17 |
18 | static void registerNatives();
19 |
20 | MainComponentsRegistry(ComponentFactory *delegate);
21 |
22 | private:
23 | static std::shared_ptr
24 | sharedProviderRegistry();
25 |
26 | static jni::local_ref initHybrid(
27 | jni::alias_ref,
28 | ComponentFactory *delegate);
29 | };
30 |
31 | } // namespace react
32 | } // namespace facebook
33 |
--------------------------------------------------------------------------------
/example/android/app/src/main/jni/OnLoad.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "MainApplicationTurboModuleManagerDelegate.h"
3 | #include "MainComponentsRegistry.h"
4 |
5 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
6 | return facebook::jni::initialize(vm, [] {
7 | facebook::react::MainApplicationTurboModuleManagerDelegate::
8 | registerNatives();
9 | facebook::react::MainComponentsRegistry::registerNatives();
10 | });
11 | }
12 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-hdpi/splashscreen_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/drawable-hdpi/splashscreen_image.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-mdpi/splashscreen_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/drawable-mdpi/splashscreen_image.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-xhdpi/splashscreen_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/drawable-xhdpi/splashscreen_image.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-xxhdpi/splashscreen_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/drawable-xxhdpi/splashscreen_image.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-xxxhdpi/splashscreen_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/drawable-xxxhdpi/splashscreen_image.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/rn_edit_text_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
21 |
22 |
23 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/splashscreen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 | #ffffff
3 | #FFFFFF
4 | #023c69
5 | #ffffff
6 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | expo-music-picker-example
3 | contain
4 | false
5 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
14 |
17 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | import org.apache.tools.ant.taskdefs.condition.Os
2 |
3 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
4 |
5 | buildscript {
6 | ext {
7 | buildToolsVersion = findProperty('android.buildToolsVersion') ?: '31.0.0'
8 | minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '21')
9 | compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '31')
10 | targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '31')
11 | if (findProperty('android.kotlinVersion')) {
12 | kotlinVersion = findProperty('android.kotlinVersion')
13 | }
14 | frescoVersion = findProperty('expo.frescoVersion') ?: '2.5.0'
15 |
16 | if (System.properties['os.arch'] == 'aarch64') {
17 | // For M1 Users we need to use the NDK 24 which added support for aarch64
18 | ndkVersion = '24.0.8215888'
19 | } else {
20 | // Otherwise we default to the side-by-side NDK version from AGP.
21 | ndkVersion = '21.4.7075529'
22 | }
23 | }
24 | repositories {
25 | google()
26 | mavenCentral()
27 | }
28 | dependencies {
29 | classpath('com.android.tools.build:gradle:7.0.4')
30 | classpath('com.facebook.react:react-native-gradle-plugin')
31 | classpath('de.undercouch:gradle-download-task:4.1.2')
32 | // NOTE: Do not place your application dependencies here; they belong
33 | // in the individual module build.gradle files
34 | }
35 | }
36 |
37 | allprojects {
38 | repositories {
39 | mavenLocal()
40 | maven {
41 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
42 | url(new File(['node', '--print', "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), '../android'))
43 | }
44 | maven {
45 | // Android JSC is installed from npm
46 | url(new File(['node', '--print', "require.resolve('jsc-android/package.json')"].execute(null, rootDir).text.trim(), '../dist'))
47 | }
48 |
49 | google()
50 | mavenCentral {
51 | // We don't want to fetch react-native from Maven Central as there are
52 | // older versions over there.
53 | content {
54 | excludeGroup 'com.facebook.react'
55 | }
56 | }
57 | maven { url 'https://www.jitpack.io' }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 |
25 | # Automatically convert third-party libraries to use AndroidX
26 | android.enableJetifier=true
27 |
28 | # Version of flipper SDK to use with React Native
29 | FLIPPER_VERSION=0.125.0
30 |
31 | # Use this property to specify which architecture you want to build.
32 | # You can also override it from the CLI using
33 | # ./gradlew -PreactNativeArchitectures=x86_64
34 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
35 |
36 | # Use this property to enable support to the new architecture.
37 | # This will allow you to use TurboModules and the Fabric render in
38 | # your application. You should enable this flag either if you want
39 | # to write custom TurboModules/Fabric components OR use libraries that
40 | # are providing them.
41 | newArchEnabled=false
42 |
43 | # The hosted JavaScript engine
44 | # Supported values: expo.jsEngine = "hermes" | "jsc"
45 | expo.jsEngine=jsc
46 |
47 | # Enable GIF support in React Native images (~200 B increase)
48 | expo.gif.enabled=true
49 | # Enable webp support in React Native images (~85 KB increase)
50 | expo.webp.enabled=true
51 | # Enable animated webp support (~3.4 MB increase)
52 | # Disabled by default because iOS doesn't support animated webp
53 | expo.webp.animated=false
54 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/example/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/example/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'expo-music-picker-example'
2 |
3 | apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle");
4 | useExpoModules()
5 |
6 | apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
7 | applyNativeModulesSettingsGradle(settings)
8 |
9 | include ':app'
10 | includeBuild(new File(["node", "--print", "require.resolve('react-native-gradle-plugin/package.json')"].execute(null, rootDir).text.trim()).getParentFile())
11 |
12 | if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") {
13 | include(":ReactAndroid")
14 | project(":ReactAndroid").projectDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../ReactAndroid");
15 | }
16 |
--------------------------------------------------------------------------------
/example/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "expo-music-picker-example",
4 | "slug": "expo-music-picker-example",
5 | "version": "1.0.0",
6 | "orientation": "portrait",
7 | "icon": "./assets/icon.png",
8 | "userInterfaceStyle": "light",
9 | "splash": {
10 | "image": "./assets/splash.png",
11 | "resizeMode": "contain",
12 | "backgroundColor": "#ffffff"
13 | },
14 | "updates": {
15 | "fallbackToCacheTimeout": 0
16 | },
17 | "assetBundlePatterns": ["**/*"],
18 | "ios": {
19 | "supportsTablet": true,
20 | "bundleIdentifier": "expo.community.modules.musicpicker.example"
21 | },
22 | "android": {
23 | "adaptiveIcon": {
24 | "foregroundImage": "./assets/adaptive-icon.png",
25 | "backgroundColor": "#FFFFFF"
26 | },
27 | "package": "expo.community.modules.musicpicker.example"
28 | },
29 | "web": {
30 | "favicon": "./assets/favicon.png"
31 | },
32 | "plugins": ["../app.plugin"]
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/example/assets/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/assets/adaptive-icon.png
--------------------------------------------------------------------------------
/example/assets/album-placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/assets/album-placeholder.png
--------------------------------------------------------------------------------
/example/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/assets/favicon.png
--------------------------------------------------------------------------------
/example/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/assets/icon.png
--------------------------------------------------------------------------------
/example/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/assets/splash.png
--------------------------------------------------------------------------------
/example/babel.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | module.exports = function (api) {
3 | api.cache(true);
4 | return {
5 | presets: ["babel-preset-expo"],
6 | plugins: [
7 | [
8 | "module-resolver",
9 | {
10 | extensions: [".tsx", ".ts", ".js", ".json"],
11 | alias: {
12 | // For development, we want to alias the library to the source
13 | ["expo-music-picker"]: path.join(
14 | __dirname,
15 | "..",
16 | "src",
17 | "ExpoMusicPicker.ts"
18 | ),
19 | },
20 | },
21 | ],
22 | ],
23 | };
24 | };
25 |
--------------------------------------------------------------------------------
/example/index.js:
--------------------------------------------------------------------------------
1 | import { registerRootComponent } from 'expo';
2 |
3 | import App from './App';
4 |
5 | // registerRootComponent calls AppRegistry.registerComponent('main', () => App);
6 | // It also ensures that whether you load the app in Expo Go or in a native build,
7 | // the environment is set up appropriately
8 | registerRootComponent(App);
9 |
--------------------------------------------------------------------------------
/example/ios/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 |
25 | # Bundle artifacts
26 | *.jsbundle
27 |
28 | # CocoaPods
29 | /Pods/
30 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
2 | require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
3 | require File.join(File.dirname(`node --print "require.resolve('@react-native-community/cli-platform-ios/package.json')"`), "native_modules")
4 |
5 | require 'json'
6 | podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {}
7 |
8 | platform :ios, podfile_properties['ios.deploymentTarget'] || '12.0'
9 | install! 'cocoapods',
10 | :deterministic_uuids => false
11 |
12 | target 'expomusicpickerexample' do
13 | use_expo_modules!
14 | config = use_native_modules!
15 |
16 | use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
17 |
18 | # Flags change depending on the env values.
19 | flags = get_default_flags()
20 |
21 | use_react_native!(
22 | :path => config[:reactNativePath],
23 | :hermes_enabled => flags[:hermes_enabled] || podfile_properties['expo.jsEngine'] == 'hermes',
24 | :fabric_enabled => flags[:fabric_enabled],
25 | # An absolute path to your application root.
26 | :app_path => "#{Dir.pwd}/.."
27 | )
28 |
29 | # Uncomment to opt-in to using Flipper
30 | # Note that if you have use_frameworks! enabled, Flipper will not work
31 | #
32 | # if !ENV['CI']
33 | # use_flipper!()
34 | # end
35 |
36 | post_install do |installer|
37 | react_native_post_install(installer)
38 | __apply_Xcode_12_5_M1_post_install_workaround(installer)
39 | end
40 |
41 | post_integrate do |installer|
42 | begin
43 | expo_patch_react_imports!(installer)
44 | rescue => e
45 | Pod::UI.warn e
46 | end
47 | end
48 |
49 | end
50 |
--------------------------------------------------------------------------------
/example/ios/Podfile.properties.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo.jsEngine": "jsc"
3 | }
4 |
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample.xcodeproj/xcshareddata/xcschemes/expomusicpickerexample.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import
4 |
5 | #import
6 |
7 | @interface AppDelegate : EXAppDelegateWrapper
8 |
9 | @end
10 |
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/AppDelegate.mm:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 |
3 | #import
4 | #import
5 | #import
6 | #import
7 | #import
8 |
9 | #import
10 |
11 | #if RCT_NEW_ARCH_ENABLED
12 | #import
13 | #import
14 | #import
15 | #import
16 | #import
17 | #import
18 |
19 | #import
20 |
21 | @interface AppDelegate () {
22 | RCTTurboModuleManager *_turboModuleManager;
23 | RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
24 | std::shared_ptr _reactNativeConfig;
25 | facebook::react::ContextContainer::Shared _contextContainer;
26 | }
27 | @end
28 | #endif
29 |
30 | @implementation AppDelegate
31 |
32 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
33 | {
34 | RCTAppSetupPrepareApp(application);
35 |
36 | RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
37 |
38 | #if RCT_NEW_ARCH_ENABLED
39 | _contextContainer = std::make_shared();
40 | _reactNativeConfig = std::make_shared();
41 | _contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
42 | _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
43 | bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
44 | #endif
45 |
46 | UIView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"main" initialProperties:nil];
47 |
48 | rootView.backgroundColor = [UIColor whiteColor];
49 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
50 | UIViewController *rootViewController = [self.reactDelegate createRootViewController];
51 | rootViewController.view = rootView;
52 | self.window.rootViewController = rootViewController;
53 | [self.window makeKeyAndVisible];
54 |
55 | [super application:application didFinishLaunchingWithOptions:launchOptions];
56 |
57 | return YES;
58 | }
59 |
60 | - (NSArray> *)extraModulesForBridge:(RCTBridge *)bridge
61 | {
62 | // If you'd like to export some custom RCTBridgeModules, add them here!
63 | return @[];
64 | }
65 |
66 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
67 | {
68 | #if DEBUG
69 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
70 | #else
71 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
72 | #endif
73 | }
74 |
75 | // Linking API
76 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options {
77 | return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options];
78 | }
79 |
80 | // Universal Links
81 | - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler {
82 | BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
83 | return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result;
84 | }
85 |
86 | // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
87 | - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
88 | {
89 | return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
90 | }
91 |
92 | // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
93 | - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
94 | {
95 | return [super application:application didFailToRegisterForRemoteNotificationsWithError:error];
96 | }
97 |
98 | // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
99 | - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
100 | {
101 | return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
102 | }
103 |
104 | #if RCT_NEW_ARCH_ENABLED
105 |
106 | #pragma mark - RCTCxxBridgeDelegate
107 |
108 | - (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge
109 | {
110 | _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
111 | delegate:self
112 | jsInvoker:bridge.jsCallInvoker];
113 | return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
114 | }
115 |
116 | #pragma mark RCTTurboModuleManagerDelegate
117 |
118 | - (Class)getModuleClassFromName:(const char *)name
119 | {
120 | return RCTCoreModulesClassProvider(name);
121 | }
122 |
123 | - (std::shared_ptr)getTurboModule:(const std::string &)name
124 | jsInvoker:(std::shared_ptr)jsInvoker
125 | {
126 | return nullptr;
127 | }
128 |
129 | - (std::shared_ptr)getTurboModule:(const std::string &)name
130 | initParams:
131 | (const facebook::react::ObjCTurboModule::InitParams &)params
132 | {
133 | return nullptr;
134 | }
135 |
136 | - (id)getModuleInstanceFromClass:(Class)moduleClass
137 | {
138 | return RCTAppSetupDefaultModuleFromClass(moduleClass);
139 | }
140 |
141 | #endif
142 |
143 | @end
144 |
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-20x20@1x.png
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-20x20@2x.png
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-20x20@3x.png
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-29x29@1x.png
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-29x29@2x.png
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-29x29@3x.png
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-40x40@1x.png
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-40x40@2x.png
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-40x40@3x.png
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-60x60@2x.png
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-60x60@3x.png
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-76x76@1x.png
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-76x76@2x.png
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "idiom": "iphone",
5 | "size": "20x20",
6 | "scale": "2x",
7 | "filename": "App-Icon-20x20@2x.png"
8 | },
9 | {
10 | "idiom": "iphone",
11 | "size": "20x20",
12 | "scale": "3x",
13 | "filename": "App-Icon-20x20@3x.png"
14 | },
15 | {
16 | "idiom": "iphone",
17 | "size": "29x29",
18 | "scale": "1x",
19 | "filename": "App-Icon-29x29@1x.png"
20 | },
21 | {
22 | "idiom": "iphone",
23 | "size": "29x29",
24 | "scale": "2x",
25 | "filename": "App-Icon-29x29@2x.png"
26 | },
27 | {
28 | "idiom": "iphone",
29 | "size": "29x29",
30 | "scale": "3x",
31 | "filename": "App-Icon-29x29@3x.png"
32 | },
33 | {
34 | "idiom": "iphone",
35 | "size": "40x40",
36 | "scale": "2x",
37 | "filename": "App-Icon-40x40@2x.png"
38 | },
39 | {
40 | "idiom": "iphone",
41 | "size": "40x40",
42 | "scale": "3x",
43 | "filename": "App-Icon-40x40@3x.png"
44 | },
45 | {
46 | "idiom": "iphone",
47 | "size": "60x60",
48 | "scale": "2x",
49 | "filename": "App-Icon-60x60@2x.png"
50 | },
51 | {
52 | "idiom": "iphone",
53 | "size": "60x60",
54 | "scale": "3x",
55 | "filename": "App-Icon-60x60@3x.png"
56 | },
57 | {
58 | "idiom": "ipad",
59 | "size": "20x20",
60 | "scale": "1x",
61 | "filename": "App-Icon-20x20@1x.png"
62 | },
63 | {
64 | "idiom": "ipad",
65 | "size": "20x20",
66 | "scale": "2x",
67 | "filename": "App-Icon-20x20@2x.png"
68 | },
69 | {
70 | "idiom": "ipad",
71 | "size": "29x29",
72 | "scale": "1x",
73 | "filename": "App-Icon-29x29@1x.png"
74 | },
75 | {
76 | "idiom": "ipad",
77 | "size": "29x29",
78 | "scale": "2x",
79 | "filename": "App-Icon-29x29@2x.png"
80 | },
81 | {
82 | "idiom": "ipad",
83 | "size": "40x40",
84 | "scale": "1x",
85 | "filename": "App-Icon-40x40@1x.png"
86 | },
87 | {
88 | "idiom": "ipad",
89 | "size": "40x40",
90 | "scale": "2x",
91 | "filename": "App-Icon-40x40@2x.png"
92 | },
93 | {
94 | "idiom": "ipad",
95 | "size": "76x76",
96 | "scale": "1x",
97 | "filename": "App-Icon-76x76@1x.png"
98 | },
99 | {
100 | "idiom": "ipad",
101 | "size": "76x76",
102 | "scale": "2x",
103 | "filename": "App-Icon-76x76@2x.png"
104 | },
105 | {
106 | "idiom": "ipad",
107 | "size": "83.5x83.5",
108 | "scale": "2x",
109 | "filename": "App-Icon-83.5x83.5@2x.png"
110 | },
111 | {
112 | "idiom": "ios-marketing",
113 | "size": "1024x1024",
114 | "scale": "1x",
115 | "filename": "ItunesArtwork@2x.png"
116 | }
117 | ],
118 | "info": {
119 | "version": 1,
120 | "author": "expo"
121 | }
122 | }
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/ios/expomusicpickerexample/Images.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "expo"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/SplashScreen.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "idiom": "universal",
5 | "filename": "image.png",
6 | "scale": "1x"
7 | },
8 | {
9 | "idiom": "universal",
10 | "scale": "2x"
11 | },
12 | {
13 | "idiom": "universal",
14 | "scale": "3x"
15 | }
16 | ],
17 | "info": {
18 | "version": 1,
19 | "author": "expo"
20 | }
21 | }
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/SplashScreen.imageset/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/ios/expomusicpickerexample/Images.xcassets/SplashScreen.imageset/image.png
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/SplashScreenBackground.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "idiom": "universal",
5 | "filename": "image.png",
6 | "scale": "1x"
7 | },
8 | {
9 | "idiom": "universal",
10 | "scale": "2x"
11 | },
12 | {
13 | "idiom": "universal",
14 | "scale": "3x"
15 | }
16 | ],
17 | "info": {
18 | "version": 1,
19 | "author": "expo"
20 | }
21 | }
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Images.xcassets/SplashScreenBackground.imageset/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/barthap/expo-music-picker/545998f2380d34aa2b68addb4bac86e787c91839/example/ios/expomusicpickerexample/Images.xcassets/SplashScreenBackground.imageset/image.png
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | expo-music-picker-example
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0.0
21 | CFBundleSignature
22 | ????
23 | CFBundleURLTypes
24 |
25 |
26 | CFBundleURLSchemes
27 |
28 | expo.community.modules.musicpicker.example
29 |
30 |
31 |
32 | CFBundleVersion
33 | 1
34 | LSRequiresIPhoneOS
35 |
36 | NSAppTransportSecurity
37 |
38 | NSAllowsArbitraryLoads
39 |
40 | NSExceptionDomains
41 |
42 | localhost
43 |
44 | NSExceptionAllowsInsecureHTTPLoads
45 |
46 |
47 |
48 |
49 | NSAppleMusicUsageDescription
50 | Allow $(PRODUCT_NAME) to access your music library
51 | UILaunchStoryboardName
52 | SplashScreen
53 | UIRequiredDeviceCapabilities
54 |
55 | armv7
56 |
57 | UIRequiresFullScreen
58 |
59 | UIStatusBarStyle
60 | UIStatusBarStyleDefault
61 | UISupportedInterfaceOrientations
62 |
63 | UIInterfaceOrientationPortrait
64 | UIInterfaceOrientationPortraitUpsideDown
65 |
66 | UISupportedInterfaceOrientations~ipad
67 |
68 | UIInterfaceOrientationPortrait
69 | UIInterfaceOrientationPortraitUpsideDown
70 | UIInterfaceOrientationLandscapeLeft
71 | UIInterfaceOrientationLandscapeRight
72 |
73 | UIUserInterfaceStyle
74 | Light
75 | UIViewControllerBasedStatusBarAppearance
76 |
77 |
78 |
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/SplashScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/Supporting/Expo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | EXUpdatesCheckOnLaunch
6 | ALWAYS
7 | EXUpdatesEnabled
8 |
9 | EXUpdatesLaunchWaitMs
10 | 0
11 | EXUpdatesSDKVersion
12 | 45.0.0
13 | EXUpdatesURL
14 | https://exp.host/@barthap10/expo-music-picker-example
15 |
16 |
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/expomusicpickerexample.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/main.m:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char * argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/example/ios/expomusicpickerexample/noop-file.swift:
--------------------------------------------------------------------------------
1 | //
2 | // @generated
3 | // A blank Swift file must be created for native modules with Swift files to work correctly.
4 | //
5 |
--------------------------------------------------------------------------------
/example/metro.config.js:
--------------------------------------------------------------------------------
1 | // Learn more https://docs.expo.io/guides/customizing-metro
2 | const { getDefaultConfig } = require('expo/metro-config');
3 | const path = require('path');
4 |
5 | const config = getDefaultConfig(__dirname);
6 |
7 | config.resolver.nodeModulesPaths = [
8 | path.resolve(__dirname, './node_modules'),
9 | path.resolve(__dirname, '../node_modules'),
10 | ];
11 |
12 | config.watchFolders = [path.resolve(__dirname, '..')];
13 |
14 | module.exports = config;
15 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "expo-music-picker-example",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "start": "expo start --dev-client",
6 | "android": "expo run:android",
7 | "ios": "expo run:ios",
8 | "web": "expo start --web"
9 | },
10 | "dependencies": {
11 | "@expo/webpack-config": "~0.16.2",
12 | "expo": "~45.0.0",
13 | "expo-splash-screen": "~0.15.1",
14 | "expo-status-bar": "~1.3.0",
15 | "react": "17.0.2",
16 | "react-dom": "17.0.2",
17 | "react-native": "0.68.2",
18 | "react-native-web": "0.17.7",
19 | "webpack-dev-server": "~3.11.0"
20 | },
21 | "devDependencies": {
22 | "@babel/core": "^7.12.9",
23 | "@types/react": "~17.0.21",
24 | "@types/react-native": "~0.66.13",
25 | "typescript": "~4.3.5"
26 | },
27 | "private": true,
28 | "expo": {
29 | "autolinking": {
30 | "nativeModulesDir": ".."
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "expo/tsconfig.base",
3 | "compilerOptions": {
4 | "strict": true,
5 | "paths": {
6 | "expo-music-picker": ["../src/ExpoMusicPicker"],
7 | "expo-music-picker/*": ["../src/*"]
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/example/webpack.config.js:
--------------------------------------------------------------------------------
1 | const createConfigAsync = require("@expo/webpack-config");
2 | const path = require("path");
3 |
4 | module.exports = async (env, argv) => {
5 | const config = await createConfigAsync(
6 | {
7 | ...env,
8 | babel: {
9 | dangerouslyAddModulePathsToTranspile: ["expo-music-picker"],
10 | },
11 | },
12 | argv
13 | );
14 | config.resolve.modules = [
15 | path.resolve(__dirname, "./node_modules"),
16 | path.resolve(__dirname, "../node_modules"),
17 | ];
18 |
19 | return config;
20 | };
21 |
--------------------------------------------------------------------------------
/expo-module.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "platforms": ["ios", "android", "web"],
3 | "ios": {
4 | "modules": ["MusicPickerModule"]
5 | },
6 | "android": {
7 | "modules": ["expo.community.modules.musicpicker.MusicPickerModule"]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/ios/ExpoMusicPicker.podspec:
--------------------------------------------------------------------------------
1 | require 'json'
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = 'ExpoMusicPicker'
7 | s.version = package['version']
8 | s.summary = package['description']
9 | s.description = package['description']
10 | s.license = package['license']
11 | s.author = package['author']
12 | s.homepage = package['homepage']
13 | s.platform = :ios, '12.0'
14 | s.swift_version = '5.4'
15 | s.source = { git: 'https://github.com/barthap/expo-music-picker' }
16 | s.static_framework = true
17 |
18 | s.dependency 'ExpoModulesCore'
19 |
20 | # Swift/Objective-C compatibility
21 | s.pod_target_xcconfig = {
22 | 'DEFINES_MODULE' => 'YES',
23 | 'SWIFT_COMPILATION_MODE' => 'wholemodule'
24 | }
25 |
26 | if !$ExpoUseSources&.include?(package['name']) && ENV['EXPO_USE_SOURCE'].to_i == 0 && File.exist?("#{s.name}.xcframework") && Gem::Version.new(Pod::VERSION) >= Gem::Version.new('1.10.0')
27 | s.source_files = "**/*.h"
28 | s.vendored_frameworks = "#{s.name}.xcframework"
29 | else
30 | s.source_files = "**/*.{h,m,swift}"
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/ios/MusicLibraryPermissionRequester.swift:
--------------------------------------------------------------------------------
1 | import ExpoModulesCore
2 | import MediaPlayer
3 |
4 | class MusicLibraryPermissionRequester: NSObject, EXPermissionsRequester {
5 | static func permissionType() -> String! {
6 | return "musicLibrary"
7 | }
8 |
9 | func requestPermissions(resolver resolve: EXPromiseResolveBlock!, rejecter reject: EXPromiseRejectBlock!) {
10 | MPMediaLibrary.requestAuthorization() { [weak self] _ in
11 | resolve(self?.getPermissions())
12 | }
13 | }
14 |
15 | func getPermissions() -> [AnyHashable : Any]! {
16 | var systemStatus: MPMediaLibraryAuthorizationStatus
17 | var status: EXPermissionStatus
18 | let musicLibraryUsageDescription = Bundle.main.object(forInfoDictionaryKey: "NSAppleMusicUsageDescription")
19 | if musicLibraryUsageDescription == nil {
20 | EXFatal(EXErrorWithMessage("""
21 | This app is missing 'NSAppleMusicUsageDescription' so music picker will fail. \
22 | Ensure that this key exists in app's Info.plist.
23 | """))
24 | systemStatus = MPMediaLibraryAuthorizationStatus.denied
25 | } else {
26 | systemStatus = MPMediaLibrary.authorizationStatus()
27 | }
28 |
29 | switch systemStatus {
30 | case .authorized:
31 | status = EXPermissionStatusGranted
32 | case .restricted,
33 | .denied:
34 | status = EXPermissionStatusDenied
35 | case .notDetermined:
36 | fallthrough
37 | @unknown default:
38 | status = EXPermissionStatusUndetermined
39 | }
40 |
41 | return [
42 | "status": status.rawValue
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/ios/MusicPickerDelegate.swift:
--------------------------------------------------------------------------------
1 | import MediaPlayer
2 | import UIKit
3 |
4 | /**
5 | Protocol that describes scenarios we care about while the user is picking media.
6 | */
7 | protocol MusicPickerResultHandler {
8 | func didPickMedia(selectedMedia: MPMediaItemCollection)
9 | func didCancelPicking()
10 | }
11 |
12 | internal class MusicPickerDelegate: NSObject,
13 | MPMediaPickerControllerDelegate,
14 | UIAdaptivePresentationControllerDelegate {
15 | private let resultHandler: MusicPickerResultHandler
16 |
17 | init(mediaPickingResultHandler: MusicPickerResultHandler) {
18 | self.resultHandler = mediaPickingResultHandler
19 | }
20 |
21 | // MARK: - Internal handlers
22 |
23 | private func handlePickedMedia(selectedMedia: MPMediaItemCollection) {
24 | self.resultHandler.didPickMedia(selectedMedia: selectedMedia)
25 | }
26 |
27 | private func handleCancelation() {
28 | self.resultHandler.didCancelPicking()
29 | }
30 |
31 | // MARK: - MPMediaPickerControllerDelegate methods
32 |
33 | public func mediaPicker(_ mediaPicker: MPMediaPickerController,
34 | didPickMediaItems mediaItemCollection: MPMediaItemCollection) {
35 | DispatchQueue.main.async {
36 | mediaPicker.dismiss(animated: true) { [weak self] in
37 | self?.handlePickedMedia(selectedMedia: mediaItemCollection)
38 | }
39 | }
40 | }
41 |
42 | func mediaPickerDidCancel(_ mediaPicker: MPMediaPickerController) {
43 | DispatchQueue.main.async {
44 | mediaPicker.dismiss(animated: true, completion: { [weak self] in
45 | self?.handleCancelation()
46 | })
47 | }
48 | }
49 |
50 | // MARK: - UIAdaptivePresentationControllerDelegate methods
51 |
52 | func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
53 | handleCancelation()
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/ios/MusicPickerExceptions.swift:
--------------------------------------------------------------------------------
1 | import ExpoModulesCore
2 |
3 | internal class MissingCurrentViewControllerException: Exception {
4 | override var reason: String {
5 | "Cannot determine currently presented view controller"
6 | }
7 | }
8 |
9 | internal class UnavailableOnSimulatorException: Exception {
10 | override var reason: String {
11 | "Music Library not available on iOS Simulator. Try on real device"
12 | }
13 | }
14 |
15 | internal class MissingPermissionException: Exception {
16 | override var reason: String {
17 | "Missing music library permission"
18 | }
19 | }
20 |
21 | internal class PermissionsModuleNotFoundException: Exception {
22 | override var reason: String {
23 | "Permissions module not found. Are you sure that Expo modules are properly linked?"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ios/MusicPickerModule.swift:
--------------------------------------------------------------------------------
1 | import ExpoModulesCore
2 | import MediaPlayer
3 |
4 | /**
5 | Helper struct storing single picking operation context variables that have their own non-sharable state.
6 | */
7 | struct PickingContext {
8 | let promise: Promise
9 | let options: MusicPickerOptions
10 | let pickerDelegate: MusicPickerDelegate
11 | }
12 |
13 | extension MPMediaItemArtwork {
14 | func base64String() -> String? {
15 | image(at: bounds.size)?.jpegData(compressionQuality: 1.0)?.base64EncodedString()
16 | }
17 | }
18 |
19 | extension MPMediaItem {
20 | func toDictionary(options: MusicPickerOptions) -> [String: Any?] {
21 | var result: [String: Any?] = [
22 | "id": persistentID,
23 | "uri": assetURL?.absoluteString,
24 | "artist": artist ?? composer,
25 | "title": title,
26 | "album": albumTitle,
27 | "durationSeconds": playbackDuration,
28 | "track": albumTrackNumber,
29 | "dateAdded": dateAdded
30 | ]
31 | if options.includeArtworkImage {
32 | result["artworkImage"] = getArtworkInfo()
33 | }
34 | return result
35 | }
36 |
37 | private func getArtworkInfo() -> [String: Any?]? {
38 | guard let artwork = self.artwork,
39 | let base64Data = artwork.base64String()
40 | else { return nil }
41 | return [
42 | "width": artwork.bounds.width,
43 | "height": artwork.bounds.height,
44 | "base64Data": base64Data
45 | ]
46 | }
47 | }
48 |
49 | public class MusicPickerModule: Module, MusicPickerResultHandler {
50 | private var currentPickingContext: PickingContext? = nil
51 |
52 | public func definition() -> ModuleDefinition {
53 | Name("ExpoMusicPicker")
54 |
55 | OnCreate {
56 | self.appContext?.permissions?.register([
57 | MusicLibraryPermissionRequester()
58 | ])
59 | }
60 |
61 | AsyncFunction("getPermissionsAsync") { (promise: Promise) -> Void in
62 | guard let permissions = self.appContext?.permissions else {
63 | return promise.reject(PermissionsModuleNotFoundException())
64 | }
65 | permissions.getPermissionUsingRequesterClass(MusicLibraryPermissionRequester.self,
66 | resolve: promise.resolver,
67 | reject: promise.legacyRejecter)
68 | }
69 |
70 | AsyncFunction("requestPermissionsAsync") { (promise: Promise) -> Void in
71 | guard let permissions = self.appContext?.permissions else {
72 | return promise.reject(PermissionsModuleNotFoundException())
73 | }
74 | permissions.askForPermission(usingRequesterClass: MusicLibraryPermissionRequester.self,
75 | resolve: promise.resolver,
76 | reject: promise.legacyRejecter)
77 | }
78 |
79 | AsyncFunction("openMusicLibraryAsync") { (options: MusicPickerOptions, promise: Promise) in
80 | #if targetEnvironment(simulator)
81 | promise.reject(UnavailableOnSimulatorException())
82 | return
83 | #else
84 | self.openMusicPicker(options: options, promise: promise)
85 | #endif
86 | }.runOnQueue(DispatchQueue.main)
87 | }
88 |
89 | private func openMusicPicker(options: MusicPickerOptions, promise: Promise) {
90 | guard Thread.isMainThread else {
91 | fatalError("ExpoMusicPicker.openMusicPicker() must be run on main thread!")
92 | }
93 |
94 | guard let currentViewController = self.appContext?.utilities?.currentViewController()
95 | else {
96 | promise.reject(MissingCurrentViewControllerException())
97 | return
98 | }
99 |
100 | guard let permissions = self.appContext?.permissions else {
101 | return promise.reject(PermissionsModuleNotFoundException())
102 | }
103 |
104 | guard permissions.hasGrantedPermission(usingRequesterClass: MusicLibraryPermissionRequester.self) else {
105 | promise.reject(MissingPermissionException())
106 | return
107 | }
108 |
109 | // TODO: Support media type selection
110 | let picker = MPMediaPickerController(mediaTypes: .anyAudio)
111 |
112 | picker.allowsPickingMultipleItems = options.allowMultipleSelection
113 | picker.showsCloudItems = options.showCloudItems
114 | picker.prompt = options.userPrompt
115 |
116 | let pickerDelegate = MusicPickerDelegate(mediaPickingResultHandler: self)
117 | let pickingContext = PickingContext(promise: promise, options: options, pickerDelegate: pickerDelegate)
118 |
119 | picker.delegate = pickingContext.pickerDelegate
120 | picker.presentationController?.delegate = pickingContext.pickerDelegate
121 |
122 | self.currentPickingContext = pickingContext
123 | currentViewController.present(picker, animated: true, completion: nil)
124 | }
125 |
126 | // MARK: - MusicPickerResultHandler
127 |
128 | func didPickMedia(selectedMedia: MPMediaItemCollection) {
129 | guard let promise = self.currentPickingContext?.promise,
130 | let options = self.currentPickingContext?.options
131 | else {
132 | NSLog("Picking operation context has been lost.")
133 | return
134 | }
135 |
136 | // Cleanup the currently stored picking context
137 | self.currentPickingContext = nil
138 |
139 | let results = selectedMedia.items.map { $0.toDictionary(options: options) }
140 | promise.resolve([
141 | "cancelled": false,
142 | "items": results
143 | ])
144 | }
145 |
146 | func didCancelPicking() {
147 | self.currentPickingContext?.promise.resolve(["cancelled": true])
148 | self.currentPickingContext = nil
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/ios/MusicPickerOptions.swift:
--------------------------------------------------------------------------------
1 | import ExpoModulesCore
2 |
3 | internal struct MusicPickerOptions: Record {
4 | @Field var allowMultipleSelection: Bool = false
5 | @Field var showCloudItems: Bool = true
6 | @Field var userPrompt: String? = nil
7 | @Field var includeArtworkImage: Bool = false
8 | }
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "expo-music-picker",
3 | "version": "0.1.1",
4 | "description": "Provides access to the system's UI for selecting audio/music from the phone's library",
5 | "main": "build/ExpoMusicPicker.js",
6 | "types": "build/ExpoMusicPicker.d.ts",
7 | "scripts": {
8 | "build": "expo-module build",
9 | "clean": "expo-module clean",
10 | "lint": "expo-module lint",
11 | "test": "expo-module test --passWithNoTests",
12 | "prepare": "expo-module prepare",
13 | "prepublishOnly": "expo-module prepublishOnly",
14 | "expo-module": "expo-module"
15 | },
16 | "keywords": [
17 | "react-native",
18 | "expo",
19 | "expo-music-picker",
20 | "music",
21 | "picker",
22 | "music-picker",
23 | "ExpoMusicPicker"
24 | ],
25 | "repository": "https://github.com/barthap/expo-music-picker",
26 | "bugs": {
27 | "url": "https://github.com/barthap/expo-music-picker/issues"
28 | },
29 | "author": "Bartłomiej Klocek (https://github.com/barthap)",
30 | "license": "MIT",
31 | "homepage": "https://github.com/barthap/expo-music-picker#readme",
32 | "dependencies": {
33 | "music-metadata-browser": "^2.5.7"
34 | },
35 | "devDependencies": {
36 | "expo-module-scripts": "^2.0.0",
37 | "expo-modules-core": "^0.10.0"
38 | },
39 | "peerDependencies": {
40 | "expo": "*",
41 | "react": "*",
42 | "react-native": "*"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/ExpoMusicPicker.ts:
--------------------------------------------------------------------------------
1 | import { PermissionResponse, UnavailabilityError } from "expo-modules-core";
2 | import { MusicPickerOptions, PickerResult } from "./ExpoMusicPicker.types";
3 |
4 | import ExpoMusicPicker from "./MusicPickerModule";
5 |
6 | export async function getPermissionsAsync(): Promise {
7 | if (!ExpoMusicPicker.getPermissionsAsync) {
8 | throw new UnavailabilityError("ExpoMusicPicker", "getPermissionsAsync");
9 | }
10 |
11 | return await ExpoMusicPicker.getPermissionsAsync();
12 | }
13 |
14 | export async function requestPermissionsAsync(): Promise {
15 | if (!ExpoMusicPicker.requestPermissionsAsync) {
16 | throw new UnavailabilityError("ExpoMusicPicker", "requestPermissionsAsync");
17 | }
18 |
19 | return await ExpoMusicPicker.requestPermissionsAsync();
20 | }
21 |
22 | export async function openMusicLibraryAsync(
23 | options: MusicPickerOptions = {}
24 | ): Promise {
25 | if (!ExpoMusicPicker.openMusicLibraryAsync) {
26 | throw new UnavailabilityError("ExpoMusicPicker", "openMusicLibraryAsync");
27 | }
28 |
29 | return await ExpoMusicPicker.openMusicLibraryAsync(options);
30 | }
31 |
32 | export * from "./ExpoMusicPicker.types";
33 |
--------------------------------------------------------------------------------
/src/ExpoMusicPicker.types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Options
3 | */
4 | export interface MusicPickerOptions {
5 | allowMultipleSelection?: boolean;
6 | includeArtworkImage?: boolean;
7 | /**
8 | * @platform ios
9 | */
10 | showCloudItems?: boolean;
11 | /**
12 | * @platform ios
13 | */
14 | userPrompt?: string;
15 | }
16 |
17 | /**
18 | * Represents single selection result
19 | */
20 | export interface MusicItem {
21 | id: number;
22 | uri: string;
23 | title?: string;
24 | artist?: string;
25 | album?: string;
26 | durationSeconds: number;
27 | track?: number;
28 | dateAdded?: number;
29 | /**
30 | * @platform android
31 | */
32 | fileName?: string;
33 | /**
34 | * @platform android
35 | */
36 | year?: number;
37 | artworkImage?: {
38 | width: number;
39 | height: number;
40 | base64Data: string;
41 | };
42 | }
43 |
44 | interface PickerResultBase {
45 | cancelled: boolean;
46 | }
47 |
48 | interface PickerResultCanceled extends PickerResultBase {
49 | cancelled: true;
50 | }
51 |
52 | interface PickerResultSuccess extends PickerResultBase {
53 | cancelled: false;
54 | items: MusicItem[];
55 | }
56 |
57 | export type PickerResult = PickerResultSuccess | PickerResultCanceled;
58 |
--------------------------------------------------------------------------------
/src/MusicPickerModule.ts:
--------------------------------------------------------------------------------
1 | import { requireNativeModule } from "expo-modules-core";
2 |
3 | export default requireNativeModule("ExpoMusicPicker");
4 |
--------------------------------------------------------------------------------
/src/MusicPickerModule.web.ts:
--------------------------------------------------------------------------------
1 | import {
2 | PermissionResponse,
3 | PermissionStatus,
4 | Platform,
5 | } from "expo-modules-core";
6 | import {
7 | MusicPickerOptions,
8 | PickerResult,
9 | MusicItem,
10 | } from "./ExpoMusicPicker.types";
11 | import musicMetadata, {
12 | IPicture,
13 | parseBlob,
14 | selectCover,
15 | } from "music-metadata-browser";
16 |
17 | export default {
18 | async getPermissionsAsync(): Promise {
19 | return {
20 | status: PermissionStatus.GRANTED,
21 | expires: "never",
22 | granted: true,
23 | canAskAgain: true,
24 | };
25 | },
26 | async requestPermissionsAsync(): Promise {
27 | return {
28 | status: PermissionStatus.GRANTED,
29 | expires: "never",
30 | granted: true,
31 | canAskAgain: true,
32 | };
33 | },
34 | async openMusicLibraryAsync(
35 | options: MusicPickerOptions
36 | ): Promise {
37 | // SSR guard
38 | if (!Platform.isDOMAvailable) {
39 | return { cancelled: true };
40 | }
41 | return await openFileBrowserAsync(options);
42 | },
43 | };
44 |
45 | function openFileBrowserAsync({
46 | allowMultipleSelection = false,
47 | includeArtworkImage = false,
48 | }: T): Promise {
49 | const input = document.createElement("input");
50 | input.style.display = "none";
51 | input.setAttribute("type", "file");
52 | input.setAttribute("accept", "audio/*");
53 | // input.setAttribute('id', v4());
54 | if (allowMultipleSelection) {
55 | input.setAttribute("multiple", "multiple");
56 | }
57 | document.body.appendChild(input);
58 |
59 | return new Promise((resolve, reject) => {
60 | input.addEventListener("change", async () => {
61 | if (input.files) {
62 | if (!allowMultipleSelection) {
63 | const item: MusicItem = await readFile(input.files[0], {
64 | includeArtworkImage,
65 | });
66 | resolve({
67 | cancelled: false,
68 | items: [item],
69 | });
70 | } else {
71 | const songs: MusicItem[] = await Promise.all(
72 | Array.from(input.files).map((file) =>
73 | readFile(file, { includeArtworkImage })
74 | )
75 | );
76 | resolve({
77 | cancelled: false,
78 | items: songs,
79 | });
80 | }
81 | }
82 | document.body.removeChild(input);
83 | });
84 |
85 | const event = new MouseEvent("click");
86 | input.dispatchEvent(event);
87 | });
88 | }
89 |
90 | async function readFile(
91 | targetFile: Blob,
92 | options: MusicPickerOptions
93 | ): Promise {
94 | const metadata = await parseBlob(targetFile, {
95 | duration: true,
96 | skipCovers: !options.includeArtworkImage,
97 | });
98 | const url = window.URL.createObjectURL(targetFile);
99 |
100 | const { title, track, album, artist, year } = metadata.common;
101 |
102 | const picture = selectCover(metadata.common.picture);
103 | console.log(picture);
104 | const cover = await parseCoverImage(picture);
105 |
106 | return {
107 | uri: url,
108 | id: -1,
109 | durationSeconds: metadata.format.duration ?? -1,
110 | album,
111 | artist,
112 | title,
113 | track: track.no ?? undefined,
114 | year,
115 | artworkImage: cover ?? undefined,
116 | };
117 | }
118 |
119 | async function parseCoverImage(image: IPicture | null): Promise<{
120 | width: number;
121 | height: number;
122 | base64Data: string;
123 | } | null> {
124 | if (!image) return null;
125 | const { data, type } = image;
126 |
127 | return new Promise((resolve, reject) => {
128 | const reader = new FileReader();
129 | reader.onerror = () => {
130 | reject(
131 | new Error(
132 | `Failed to read the selected media because the operation failed.`
133 | )
134 | );
135 | };
136 | reader.onload = ({ target }) => {
137 | const uri = (target as any).result;
138 | const fail = () => resolve(null);
139 |
140 | if (typeof uri === "string") {
141 | const image = new Image();
142 | // for some reason, the library returns image base64 data,
143 | // but with application/octet-stream mime type
144 | image.src = uri.replace(
145 | "application/octet-stream",
146 | type ?? "image/jpeg"
147 | );
148 | image.onload = () =>
149 | resolve({
150 | width: image.naturalWidth ?? image.width,
151 | height: image.naturalHeight ?? image.height,
152 |
153 | // The blob's result cannot be directly decoded as Base64 without
154 | // first removing the Data-URL declaration preceding the
155 | // Base64-encoded data. To retrieve only the Base64 encoded string,
156 | // first remove data:*/*;base64, from the result.
157 | // https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
158 | base64Data: uri.substr(uri.indexOf(",") + 1),
159 | });
160 | image.onerror = () => fail;
161 | } else {
162 | fail();
163 | }
164 | };
165 |
166 | reader.readAsDataURL(new Blob([new Uint8Array(data)]));
167 | });
168 | }
169 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | // @generated by expo-module-scripts
2 | {
3 | "extends": "expo-module-scripts/tsconfig.base",
4 | "compilerOptions": {
5 | "outDir": "./build"
6 | },
7 | "include": ["./src"],
8 | "exclude": ["**/__mocks__/*", "**/__tests__/*"]
9 | }
10 |
--------------------------------------------------------------------------------