├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── android
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── io
│ └── cmichel
│ └── appLauncher
│ ├── AlarmReceiver.java
│ ├── LauncherModule.java
│ └── LauncherPackage.java
├── index.android.js
├── index.ios.js
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build/
3 | npm-debug.log
4 | system-notification.iml
5 | react-native-system-notification.iml
6 | .DS_Store
7 | .idea/
8 | android/.idea/
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | android/build
3 | android/build/*
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Christoph Michel
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Currently under development
2 |
3 | ## Installation
4 | * Run `npm install --save git+https://github.com/MrToph/react-native-app-launcher.git`
5 | * Add the following to `android/settings.gradle`:
6 | ```
7 | include ':react-native-app-launcher'
8 | project(':react-native-app-launcher').projectDir = new File(settingsDir, '../node_modules/react-native-app-launcher/android')
9 | ```
10 |
11 | * Add the following to `android/app/build.gradle`:
12 | ```xml
13 | ...
14 |
15 | dependencies {
16 | ...
17 | compile project(':react-native-app-launcher') // react-native-app-launcher
18 | }
19 | ```
20 | * Add the following to `android/app/src/main/AndroidManifest.xml`:
21 | ```xml
22 |
24 |
25 |
26 |
27 |
32 |
33 | ...
34 |
35 |
36 |
37 |
38 |
39 |
40 | ```
41 | * Add the following to `android/app/src/main/java/**/MainApplication.java`:
42 | ```java
43 | package com.motivation;
44 |
45 | import io.cmichel.appLauncher.LauncherPackage; // add this for react-native-app-launcher
46 |
47 | public class MainApplication extends Application implements ReactApplication {
48 |
49 | @Override
50 | protected List getPackages() {
51 | return Arrays.asList(
52 | new MainReactPackage(),
53 | new LauncherPackage() // add this for react-native-app-launcher
54 | );
55 | }
56 | }
57 | ```
58 |
59 |
60 | ## Mofifying/Build the Project in Android Studio
61 | * Start `Android Studio` and select `File -> New -> Import Project` and select the `android` folder.
62 | * If you get a `Plugin with id 'android-library' not found` Error, install `android support repository`
63 | through the SDK manager.
64 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | import groovy.json.JsonSlurper
2 |
3 | def computeVersionName() {
4 | // dynamically retrieve version from package.json
5 | def slurper = new JsonSlurper()
6 | def json = slurper.parse(file('../package.json'), "utf-8")
7 | return json.version
8 | }
9 |
10 | buildscript {
11 | repositories {
12 | jcenter()
13 | }
14 |
15 | dependencies {
16 | classpath 'com.android.tools.build:gradle:1.5.0'
17 | }
18 | }
19 |
20 | apply plugin: 'com.android.library'
21 |
22 | android {
23 | compileSdkVersion 23
24 | buildToolsVersion "23.0.1"
25 |
26 | defaultConfig {
27 | minSdkVersion 16
28 | targetSdkVersion 22
29 | versionCode 1
30 | // get version name from package.json version
31 | versionName computeVersionName()
32 | }
33 | lintOptions {
34 | abortOnError false
35 | }
36 | }
37 |
38 | repositories {
39 | mavenCentral()
40 | }
41 |
42 | dependencies {
43 | compile 'com.facebook.react:react-native:+'
44 | }
45 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/android/src/main/java/io/cmichel/appLauncher/AlarmReceiver.java:
--------------------------------------------------------------------------------
1 | package io.cmichel.appLauncher;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.util.Log;
7 |
8 | public class AlarmReceiver extends BroadcastReceiver {
9 | @Override
10 | public void onReceive(Context context, Intent intent) {
11 | String alarmID = intent.getAction();
12 | launchApplication(context, alarmID);
13 | }
14 |
15 | private void launchApplication(Context context, String alarmID) {
16 | String packageName = context.getApplicationContext().getPackageName();
17 | Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
18 |
19 | launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
20 | launchIntent.putExtra("alarmID", alarmID);
21 |
22 | context.startActivity(launchIntent);
23 | Log.i("ReactNativeAppLauncher", "AlarmReceiver: Launching: " + packageName);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/android/src/main/java/io/cmichel/appLauncher/LauncherModule.java:
--------------------------------------------------------------------------------
1 | package io.cmichel.appLauncher;
2 |
3 | import android.app.AlarmManager;
4 | import android.app.PendingIntent;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.net.Uri;
8 | import android.os.Build;
9 |
10 | import com.facebook.react.bridge.ReactApplicationContext;
11 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
12 | import com.facebook.react.bridge.ReactMethod;
13 |
14 | import java.util.HashMap;
15 | import java.util.Map;
16 |
17 | public class LauncherModule extends ReactContextBaseJavaModule {
18 |
19 | public LauncherModule(ReactApplicationContext reactContext) {
20 | super(reactContext);
21 | }
22 |
23 | @Override
24 | public String getName() {
25 | return "AppLauncher";
26 | }
27 |
28 | @Override
29 | public Map getConstants() {
30 | final Map constants = new HashMap<>();
31 | return constants;
32 | }
33 |
34 | /**
35 | * Creates or overwrites an alarm that launches the main application at the specified timestamp.
36 | * You can set multiple alarms by using different ids.
37 | * @param id The id identifying this alarm.
38 | * @param timestamp When to fire off the alarm.
39 | * @param inexact Determines if the alarm should be inexact to save on battery power.
40 | */
41 | @ReactMethod
42 | public final void setAlarm(String id, double timestamp, boolean inexact) {
43 | PendingIntent pendingIntent = createPendingIntent(id);
44 | long timestampLong = (long)timestamp; // React Bridge doesn't understand longs
45 | // get the alarm manager, and schedule an alarm that calls the receiver
46 | // We will use setAlarmClock because we want an indicator to show in the status bar.
47 | // If you want to modify it and are unsure what to method to use, check https://plus.google.com/+AndroidDevelopers/posts/GdNrQciPwqo
48 | if(!inexact) {
49 | // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
50 | // getAlarmManager().setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timestampLong, pendingIntent);
51 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
52 | getAlarmManager().setAlarmClock(new AlarmManager.AlarmClockInfo(timestampLong, pendingIntent), pendingIntent);
53 | else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
54 | getAlarmManager().setExact(AlarmManager.RTC_WAKEUP, timestampLong, pendingIntent);
55 | else
56 | getAlarmManager().set(AlarmManager.RTC_WAKEUP, timestampLong, pendingIntent);
57 | } else {
58 | getAlarmManager().set(AlarmManager.RTC_WAKEUP, timestampLong, pendingIntent);
59 | }
60 | Context context = getReactApplicationContext();
61 | }
62 |
63 | @ReactMethod
64 | public final void clearAlarm(String id) {
65 | PendingIntent pendingIntent = createPendingIntent(id);
66 | getAlarmManager().cancel(pendingIntent);
67 | }
68 |
69 | private PendingIntent createPendingIntent(String id) {
70 | Context context = getReactApplicationContext();
71 | // create the pending intent
72 | Intent intent = new Intent(context, AlarmReceiver.class);
73 | // set unique alarm ID to identify it. Used for clearing and seeing which one fired
74 | // public boolean filterEquals(Intent other) compare the action, data, type, package, component, and categories, but do not compare the extra
75 | intent.setData(Uri.parse("id://" + id));
76 | intent.setAction(String.valueOf(id));
77 | return PendingIntent.getBroadcast(context, 0, intent, 0);
78 | }
79 |
80 | private AlarmManager getAlarmManager() {
81 | return (AlarmManager) getReactApplicationContext().getSystemService(Context.ALARM_SERVICE);
82 | }
83 | }
--------------------------------------------------------------------------------
/android/src/main/java/io/cmichel/appLauncher/LauncherPackage.java:
--------------------------------------------------------------------------------
1 | package io.cmichel.appLauncher;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.JavaScriptModule;
5 | import com.facebook.react.bridge.NativeModule;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.react.uimanager.ViewManager;
8 |
9 | import java.util.ArrayList;
10 | import java.util.Collections;
11 | import java.util.List;
12 |
13 | public class LauncherPackage implements ReactPackage {
14 |
15 | public List> createJSModules() {
16 | return Collections.emptyList();
17 | }
18 |
19 | public List createViewManagers(ReactApplicationContext reactContext) {
20 | return Collections.emptyList();
21 | }
22 |
23 | public List createNativeModules(ReactApplicationContext reactContext) {
24 | List modules = new ArrayList<>();
25 | modules.add(new LauncherModule(reactContext));
26 | return modules;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/index.android.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import { NativeModules } from 'react-native'
4 |
5 | export default class AppLauncher {
6 | /**
7 | * Sets or updates an alarm through Android's AlarmManager system with the specified `id` to fire at the specified
8 | * UNIX timestamp `timestamp`. If parameter `inexact` is set to true, the battery-saving version of
9 | * AlarmManager is used that can fire at inexact times. Default value for `inexact` is false.
10 | */
11 | static setAlarm(id, timestamp, inexact){
12 | if(!(typeof id === 'string' || typeof id === 'number'))
13 | throw new Error('AppLauncher.setAlarm: `id` must be a number or a string.')
14 | if(typeof timestamp !== 'number')
15 | throw new Error('AppLauncher.setAlarm: `timestamp` must be an integer.')
16 | id = id.toString() // parse to string
17 | timestamp = Math.trunc(timestamp) // parse to int
18 | inexact = !!inexact // parse to bool
19 | NativeModules.AppLauncher.setAlarm(id, timestamp, inexact)
20 | }
21 |
22 | /**
23 | * Clears the alarm specified by `id`
24 | */
25 | static clearAlarm(id) {
26 | if(!(typeof id === 'string' || typeof id === 'number'))
27 | throw new Error('AppLauncher.clearAlarm: `id` must be a number or a string.')
28 | id = id.toString() // parse to string
29 | NativeModules.AppLauncher.clearAlarm(id)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/index.ios.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var fail = 'Cannot use react-native-app-launcher on IOS.';
4 | class AppLauncher {
5 |
6 | static setAlarm() {
7 | console.warn(fail);
8 | }
9 |
10 | static clearAlarm() {
11 | console.warn(fail);
12 | }
13 |
14 | }
15 |
16 | module.exports = AppLauncher;
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-app-launcher",
3 | "version": "0.0.1",
4 | "author": "Christoph Michel http://cmichel.io",
5 | "private": false,
6 | "license": "MIT",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/MrToph/react-native-app-launcher.git"
10 | },
11 | "bugs": {
12 | "url": "https://github.com/MrToph/react-native-app-launcher/issues"
13 | },
14 | "homepage": "https://github.com/MrToph/react-native-app-launcher",
15 | "keywords": [
16 | "react-native",
17 | "android",
18 | "launcher",
19 | "activity",
20 | "alarmmanager"
21 | ],
22 | "scripts": {
23 | "start": "node node_modules/react-native/local-cli/cli.js start",
24 | "test": "jest"
25 | },
26 | "peerDependencies": {
27 | "react-native": ">=0.38.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------