├── .gitignore ├── FacebookNotifications-debug.apk ├── FacebookNotifications-release.apk ├── FacebookNotifications.iml ├── LICENSE ├── README.md ├── app ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── org │ │ └── surrel │ │ └── facebooknotifications │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── ic_launcher-web.svg │ ├── ic_notif.png │ ├── ic_notif.svg │ ├── ic_notif.xcf │ ├── java │ │ └── org │ │ │ └── surrel │ │ │ └── facebooknotifications │ │ │ ├── BootReceiver.java │ │ │ ├── MainActivity.java │ │ │ ├── PrefsActivity.java │ │ │ ├── UpdateService.java │ │ │ └── WakeupManager.java │ └── res │ │ ├── drawable-hdpi-v11 │ │ └── ic_notification.png │ │ ├── drawable-hdpi-v9 │ │ └── ic_notification.png │ │ ├── drawable-hdpi │ │ ├── ic_menu_groups.png │ │ ├── ic_menu_invite.png │ │ ├── ic_menu_mapmode.png │ │ ├── ic_menu_start_conversation.png │ │ └── ic_notification.png │ │ ├── drawable-mdpi-v11 │ │ └── ic_notification.png │ │ ├── drawable-mdpi-v9 │ │ └── ic_notification.png │ │ ├── drawable-mdpi │ │ ├── ic_menu_groups.png │ │ ├── ic_menu_invite.png │ │ ├── ic_menu_mapmode.png │ │ ├── ic_menu_start_conversation.png │ │ └── ic_notification.png │ │ ├── drawable-xhdpi-v11 │ │ └── ic_notification.png │ │ ├── drawable-xhdpi-v9 │ │ └── ic_notification.png │ │ ├── drawable-xhdpi │ │ ├── ic_menu_groups.png │ │ ├── ic_menu_invite.png │ │ ├── ic_menu_mapmode.png │ │ ├── ic_menu_start_conversation.png │ │ └── ic_notification.png │ │ ├── drawable-xxhdpi-v11 │ │ └── ic_notification.png │ │ ├── drawable-xxhdpi-v9 │ │ └── ic_notification.png │ │ ├── drawable-xxhdpi │ │ ├── ic_menu_groups.png │ │ ├── ic_menu_invite.png │ │ ├── ic_menu_mapmode.png │ │ ├── ic_menu_start_conversation.png │ │ └── ic_notification.png │ │ ├── layout │ │ └── activity_main.xml │ │ ├── menu │ │ └── main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ ├── values │ │ ├── arrays.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ └── strings.xml │ │ └── xml │ │ └── preferences.xml │ └── test │ └── java │ └── org │ └── surrel │ └── facebooknotifications │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .idea 10 | build 11 | .*.swp 12 | -------------------------------------------------------------------------------- /FacebookNotifications-debug.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/FacebookNotifications-debug.apk -------------------------------------------------------------------------------- /FacebookNotifications-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/FacebookNotifications-release.apk -------------------------------------------------------------------------------- /FacebookNotifications.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 gsurrel 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Notifications for Facebook 2 | 3 | Small Android app for getting notified (friends, messages and notifications) while being privacy and battery friendly. It doesn't require the Facebook Platform to be turned on. It plays nicely with [SlimSocial for Facebook](https://f-droid.org/repository/browse/?fdfilter=SlimSOcial&fdid=it.rignanese.leo.slimfacebook). 4 | 5 | 6 | Get it on F-Droid 7 | 8 | Get it on Google Play 9 | 10 | [Direct link to release .apk file](https://raw.githubusercontent.com/gsurrel/FacebookNotifications/master/FacebookNotifications-release.apk) and [link to debug .apk file](https://raw.githubusercontent.com/gsurrel/FacebookNotifications/master/FacebookNotifications-debug.apk). 11 | 12 | ![Notifications for Facebook logo image](https://raw.githubusercontent.com/gsurrel/FacebookNotifications/master/app/src/main/ic_launcher-web.png) 13 | 14 | ## Changelog 15 | 16 | * _1.10.1_ 17 | * Upgrade API level for the Play Store to be happy 18 | * Add link to DontKillMyApp 19 | * Ask runtime permission to draw over other apps 20 | 21 | * _1.10.0_ 22 | * Added shortcut to logout 23 | * Added quit button 24 | * Added a "Open in browser" button 25 | 26 | * _1.9.2_ 27 | * Fixed a crash on wrong user input 28 | 29 | * _1.9.1_ 30 | * Hopefully fixed issues #19 & #21 31 | 32 | * _1.9.0_ thanks to @hirvinen 33 | * Fixed issues #18 & #20 34 | * Cleaned and refactored code 35 | * Added possibility to share the current page 36 | 37 | * _1.8.0_ 38 | * Fixed custom vibration pattern bug 39 | * Added custom blinking pattern per category 40 | * Updated sdk build-tools used 41 | 42 | * _1.7.0_ 43 | * Added compatibility to older devices 44 | 45 | * _1.6.5_ 46 | * Added vibration control 47 | * Settings panel disabled irrelevant features 48 | 49 | * _1.6.3_ 50 | * Made app Play Store compliant 51 | 52 | * _1.6.2_ 53 | * Custom update time added in the settings 54 | 55 | * _1.6.1_ 56 | * Simplified preferences page, allowing to disable sound notification by selecting the "None" sound 57 | 58 | * _1.6.0_ 59 | * Added sound notification 60 | * Enable/disable notifications per type 61 | * Support back button while browsing 62 | 63 | * _1.5.x_ 64 | * Removed support library without API level regression 65 | * F-Droid integration 66 | * Not pausing current activity when updating 67 | * Can be used as a browser for Facebook. Not fully featured! 68 | * Better code construct 69 | * Unified notification type using "Big View" and direct access buttons 70 | * Remembers the notification count so it does not bother the user every 5 minutes 71 | * Using notification LED 72 | 73 | * _1.0.0_ 74 | * Initial release 75 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdk 34 5 | defaultConfig { 6 | applicationId "org.surrel.facebooknotifications" 7 | minSdkVersion 16 8 | targetSdkVersion 34 9 | versionCode 14 10 | versionName '1.10.1' 11 | buildConfigField "long", "TIMESTAMP", System.currentTimeMillis() + "L" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | debug { 19 | minifyEnabled false 20 | } 21 | } 22 | productFlavors { 23 | } 24 | namespace 'org.surrel.facebooknotifications' 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_11 27 | targetCompatibility JavaVersion.VERSION_11 28 | } 29 | buildFeatures { 30 | buildConfig true 31 | } 32 | } 33 | 34 | dependencies { 35 | implementation fileTree(include: ['*.jar'], dir: 'libs') 36 | implementation 'androidx.appcompat:appcompat:1.6.1' 37 | implementation 'androidx.concurrent:concurrent-futures:1.1.0' 38 | } 39 | -------------------------------------------------------------------------------- /app/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 21 11:34:03 PDT 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip 7 | -------------------------------------------------------------------------------- /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 /Users/surrel/Library/Android/sdk/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 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/org/surrel/facebooknotifications/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package org.surrel.facebooknotifications; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 36 | 37 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 24 | 27 | 31 | 35 | 39 | 43 | 47 | 51 | 52 | 59 | 68 | 71 | 75 | 79 | 83 | 87 | 88 | 98 | 108 | 109 | 127 | 129 | 130 | 132 | image/svg+xml 133 | 135 | 136 | 137 | 138 | 139 | 144 | 147 | 149 | 155 | 162 | 168 | 169 | 172 | 179 | 185 | 187 | 194 | 199 | 204 | 209 | 214 | 219 | 224 | 229 | 234 | 239 | 244 | 249 | 254 | 259 | 264 | 269 | 274 | 279 | 284 | 289 | 294 | 300 | 305 | 310 | 315 | 320 | 325 | 330 | 335 | 340 | 346 | 351 | 356 | 361 | 366 | 371 | 377 | 378 | 379 | 382 | 388 | 395 | # 405 | 406 | 409 | 415 | 422 | 424 | 430 | 436 | 437 | 438 | 444 | 450 | 451 | 457 | 458 | 459 | -------------------------------------------------------------------------------- /app/src/main/ic_notif.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/ic_notif.png -------------------------------------------------------------------------------- /app/src/main/ic_notif.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 11 | 18 | 19 | 32 | 34 | 35 | 37 | image/svg+xml 38 | 40 | 41 | 42 | 43 | 44 | 49 | 55 | 60 | 65 | 69 | 75 | 76 | 77 | 83 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /app/src/main/ic_notif.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/ic_notif.xcf -------------------------------------------------------------------------------- /app/src/main/java/org/surrel/facebooknotifications/BootReceiver.java: -------------------------------------------------------------------------------- 1 | package org.surrel.facebooknotifications; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | public class BootReceiver extends BroadcastReceiver { 8 | @Override 9 | public void onReceive(Context context, Intent intent) { 10 | if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) { 11 | WakeupManager.updateNotificationSystem(context); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/org/surrel/facebooknotifications/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.surrel.facebooknotifications; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Activity; 5 | import android.app.AlarmManager; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.net.Uri; 9 | import android.os.Build; 10 | import android.os.Bundle; 11 | import android.preference.PreferenceManager; 12 | import android.provider.Settings; 13 | import android.view.KeyEvent; 14 | import android.view.Menu; 15 | import android.view.MenuItem; 16 | import android.webkit.WebSettings; 17 | import android.webkit.WebView; 18 | import android.webkit.WebViewClient; 19 | import android.widget.Toast; 20 | 21 | import androidx.core.content.ContextCompat; 22 | import androidx.core.content.IntentCompat; 23 | import androidx.core.content.PackageManagerCompat; 24 | import androidx.core.content.UnusedAppRestrictionsConstants; 25 | 26 | import com.google.common.util.concurrent.ListenableFuture; 27 | 28 | import java.util.concurrent.ExecutionException; 29 | 30 | public class MainActivity extends Activity { 31 | private static final int REQUEST_CODE = 1; // Replace with your request code 32 | 33 | public static final int AlarmType = AlarmManager.ELAPSED_REALTIME_WAKEUP; 34 | public static final int SETTINGS_MENU = 0; 35 | public static final String FB_URL = "https://m.facebook.com"; 36 | 37 | private WebView webview; 38 | 39 | @SuppressLint({"SetJavaScriptEnabled", "AddJavascriptInterface", "JavascriptInterface"}) 40 | @Override 41 | protected void onCreate(Bundle savedInstanceState) { 42 | super.onCreate(savedInstanceState); 43 | 44 | checkAppRestrictions(this); 45 | 46 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 47 | if (!Settings.canDrawOverlays(this)) { 48 | Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); 49 | startActivityForResult(intent, 0); 50 | } 51 | } 52 | 53 | PreferenceManager.setDefaultValues(this, R.xml.preferences, false); 54 | setContentView(R.layout.activity_main); 55 | 56 | String targetURL = FB_URL; 57 | 58 | if (getIntent().getExtras() != null) { 59 | String url = getIntent().getExtras().getString("url", ""); 60 | if (!"".equals(url)) { 61 | targetURL = url; 62 | } 63 | } 64 | 65 | WakeupManager.updateNotificationSystem(this); 66 | 67 | webview = new WebView(this); 68 | webview.setWebViewClient(new WebViewClient()); 69 | webview.loadData("

" + getString(R.string.request_pending) + "

", "text/html", "UTF-8"); 70 | webview.getSettings().setJavaScriptEnabled(true); 71 | webview.addJavascriptInterface(this, "customInterface"); 72 | webview.setWebViewClient(new WebViewClient() { 73 | @Override 74 | public void onPageFinished(WebView view, String url) { 75 | // Javascript URL injection is defined in UpdateService 76 | webview.loadUrl("javascript:;"); 77 | } 78 | }); 79 | 80 | WebSettings webSettings = webview.getSettings(); 81 | webSettings.setBlockNetworkImage(true); 82 | webSettings.setUserAgentString(getString(R.string.app_name)); 83 | webview.loadUrl(targetURL); 84 | setContentView(webview); 85 | _dMsg("Debug build, timestamp " + BuildConfig.TIMESTAMP); 86 | } 87 | 88 | private void checkAppRestrictions(Context context) { 89 | ListenableFuture future = PackageManagerCompat.getUnusedAppRestrictionsStatus(context); 90 | future.addListener(() -> { 91 | try { 92 | int result = future.get(); 93 | onResult(result); 94 | } catch (ExecutionException | InterruptedException e) { 95 | e.printStackTrace(); 96 | } 97 | }, ContextCompat.getMainExecutor(context)); 98 | } 99 | 100 | private void onResult(int appRestrictionsStatus) { 101 | switch (appRestrictionsStatus) { 102 | case UnusedAppRestrictionsConstants.ERROR: 103 | case UnusedAppRestrictionsConstants.FEATURE_NOT_AVAILABLE: 104 | case UnusedAppRestrictionsConstants.DISABLED: 105 | break; 106 | case UnusedAppRestrictionsConstants.API_30_BACKPORT: 107 | case UnusedAppRestrictionsConstants.API_30: 108 | case UnusedAppRestrictionsConstants.API_31: 109 | handleRestrictions(); 110 | break; 111 | } 112 | } 113 | 114 | private void handleRestrictions() { 115 | Intent intent = IntentCompat.createManageUnusedAppRestrictionsIntent(this, getPackageName()); 116 | startActivityForResult(intent, REQUEST_CODE); 117 | } 118 | 119 | @Override 120 | public boolean onKeyDown(int keyCode, KeyEvent event) { 121 | if (event.getAction() == KeyEvent.ACTION_DOWN) { 122 | if (keyCode == KeyEvent.KEYCODE_BACK) { 123 | if (webview.canGoBack()) { 124 | webview.goBack(); 125 | } else { 126 | finish(); 127 | } 128 | return true; 129 | } 130 | } 131 | return super.onKeyDown(keyCode, event); 132 | } 133 | 134 | @Override 135 | public boolean onCreateOptionsMenu(Menu menu) { 136 | getMenuInflater().inflate(R.menu.main, menu); 137 | return super.onCreateOptionsMenu(menu); 138 | } 139 | 140 | @Override 141 | public boolean onOptionsItemSelected(MenuItem item) { 142 | if (item.getItemId() == R.id.menu_item_settings) { 143 | startActivityForResult(new Intent(MainActivity.this, PrefsActivity.class), SETTINGS_MENU); 144 | } else if (item.getItemId() == R.id.menu_item_quit) { 145 | finish(); 146 | } else if (item.getItemId() == R.id.menu_item_problems) { 147 | Intent dontkillmyapp = new Intent(Intent.ACTION_VIEW, Uri.parse("https://dontkillmyapp.com/")); 148 | startActivity(dontkillmyapp); 149 | } 150 | return true; 151 | } 152 | 153 | protected void _dMsg(CharSequence text) { 154 | if (BuildConfig.BUILD_TYPE.equals("debug")) { 155 | Toast.makeText(this, text, Toast.LENGTH_SHORT).show(); 156 | } 157 | } 158 | 159 | @Override 160 | public void onPause() { 161 | super.onPause(); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /app/src/main/java/org/surrel/facebooknotifications/PrefsActivity.java: -------------------------------------------------------------------------------- 1 | package org.surrel.facebooknotifications; 2 | 3 | import android.content.SharedPreferences; 4 | import android.os.Bundle; 5 | import android.preference.EditTextPreference; 6 | import android.preference.PreferenceActivity; 7 | import android.preference.PreferenceManager; 8 | import android.util.Log; 9 | import android.widget.Toast; 10 | 11 | public class PrefsActivity extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener { 12 | private EditTextPreference updatePreference; 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | //It is deprecated but it will work, it is still only way for lower android versions 18 | addPreferencesFromResource(R.xml.preferences); 19 | PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this); 20 | updatePreference = (EditTextPreference) findPreference(WakeupManager.UPDATE_INTERVAL); 21 | } 22 | 23 | @Override 24 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 25 | Log.d("fbn.PrefsActivity", key); 26 | switch (key) { 27 | case WakeupManager.UPDATE_INTERVAL: 28 | String newIntervalStr = sharedPreferences.getString(key, null); 29 | int newInterval = WakeupManager.DEFAULT_UPDATE_INTERVAL; 30 | boolean updateIntervalSetting = false; 31 | try{ 32 | assert newIntervalStr != null; 33 | newInterval = Integer.parseInt(newIntervalStr); 34 | }catch (NumberFormatException e){ 35 | newIntervalStr = null; 36 | } 37 | if (newIntervalStr == null || newIntervalStr.isEmpty() || newIntervalStr.matches("\\D")) { 38 | updateIntervalSetting = true; 39 | Toast.makeText(this, getString(R.string.update_interval_using_default) + newInterval, Toast.LENGTH_SHORT).show(); 40 | } else { 41 | if (newInterval > WakeupManager.MAX_UPDATE_INTERVAL) { 42 | updateIntervalSetting = true; 43 | newInterval = WakeupManager.MAX_UPDATE_INTERVAL; 44 | Toast.makeText(this, getString(R.string.update_interval_using_max) + newInterval, Toast.LENGTH_SHORT).show(); 45 | } else if (newInterval < WakeupManager.MIN_UPDATE_INTERVAL) { 46 | updateIntervalSetting = true; 47 | newInterval = WakeupManager.MIN_UPDATE_INTERVAL; 48 | Toast.makeText(this, getString(R.string.update_interval_using_min) + newInterval, Toast.LENGTH_SHORT).show(); 49 | } 50 | } 51 | if (updateIntervalSetting) { 52 | updatePreference.setText(Integer.toString(newInterval)); 53 | // Above relies on a deprecated method to get preference object. 54 | // Below doesn't need that, but the value shown in editor doesn't get updated before leaving preferences 55 | // sharedPreferences.edit().putString(key, Integer.toString(newInterval)).apply(); 56 | } 57 | break; 58 | case WakeupManager.ENABLE_NOTIFICATION_SYNCHRO: 59 | Log.d("fbn.PrefsActivity", "Calling WakeupManager"); 60 | WakeupManager.updateNotificationSystem(this); 61 | break; 62 | default: 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/org/surrel/facebooknotifications/UpdateService.java: -------------------------------------------------------------------------------- 1 | package org.surrel.facebooknotifications; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Notification; 5 | import android.app.NotificationChannel; 6 | import android.app.NotificationManager; 7 | import android.app.PendingIntent; 8 | import android.app.Service; 9 | import android.app.TaskStackBuilder; 10 | import android.content.Context; 11 | import android.content.Intent; 12 | import android.content.SharedPreferences; 13 | import android.graphics.Bitmap; 14 | import android.graphics.BitmapFactory; 15 | import android.graphics.Color; 16 | import android.graphics.PixelFormat; 17 | import android.net.Uri; 18 | import android.os.Build; 19 | import android.os.IBinder; 20 | import android.preference.PreferenceManager; 21 | import android.provider.Settings; 22 | import android.util.Log; 23 | import android.view.Gravity; 24 | import android.view.View; 25 | import android.view.ViewGroup; 26 | import android.view.WindowManager; 27 | import android.webkit.JavascriptInterface; 28 | import android.webkit.WebSettings; 29 | import android.webkit.WebView; 30 | import android.webkit.WebViewClient; 31 | 32 | import org.json.JSONException; 33 | import org.json.JSONObject; 34 | 35 | import java.util.Objects; 36 | 37 | public class UpdateService extends Service { 38 | private static final int NOTIF_BASE = 0; 39 | private static final int NOTIF_LOGIN = NOTIF_BASE + 1; 40 | private static final int NOTIF_UNIFIED = NOTIF_LOGIN + 1; 41 | 42 | private static final String URL_HOME = "https://m.facebook.com/"; 43 | private static final String URL_BOOKMARKS = URL_HOME + "menu/bookmarks/"; 44 | private static final String URL_FRIEND_REQUESTS = URL_HOME + "friends/center/requests/"; 45 | private static final String URL_MESSAGES = URL_HOME + "messages/"; 46 | private static final String URL_GROUPS = URL_HOME + "groups/"; 47 | private static final String URL_NOTIFICATIONS = URL_HOME + "notifications.php"; 48 | 49 | private static final String PREF_FRIEND_REQUESTS = "notification_friends"; 50 | private static final String PREF_MESSAGES = "notification_messages"; 51 | private static final String PREF_GROUPS = "notification_groups"; 52 | private static final String PREF_NOTIFICATIONS = "notification_notifications"; 53 | 54 | private static final String PREF_SOUND_FRIEND_REQUESTS = "notification_sound_choice_friends"; 55 | private static final String PREF_SOUND_MESSAGES = "notification_sound_choice_messages"; 56 | private static final String PREF_SOUND_NOTIFICATIONS = "notification_sound_choice_notifications"; 57 | private static final String PREF_SOUND_GROUPS = "notification_sound_choice_groups"; 58 | 59 | private static final String PREF_VIBRATE_FRIEND_REQUESTS = "notification_vibrate_choice_friends"; 60 | private static final String PREF_VIBRATE_MESSAGES = "notification_vibrate_choice_messages"; 61 | private static final String PREF_VIBRATE_GROUPS = "notification_vibrate_choice_groups"; 62 | private static final String PREF_VIBRATE_NOTIFICATIONS = "notification_vibrate_choice_notifications"; 63 | 64 | private static final String PREF_BLINK_FRIEND_REQUESTS = "notification_blink_rate_choice_friends"; 65 | private static final String PREF_BLINK_MESSAGES = "notification_blink_rate_choice_messages"; 66 | private static final String PREF_BLINK_GROUPS = "notification_blink_rate_choice_groups"; 67 | private static final String PREF_BLINK_NOTIFICATIONS = "notification_blink_rate_choice_notifications"; 68 | 69 | private static final String KEY_VIBRATE_SHORT = "vibrate_short"; 70 | private static final String KEY_VIBRATE_LONG = "vibrate_long"; 71 | private static final String KEY_VIBRATE_DOUBLE = "vibrate_double"; 72 | private static final String KEY_VIBRATE_DOUBLE_LONG = "vibrate_double_long"; 73 | 74 | private static final String KEY_BLINK_NORMAL = "normal"; 75 | private static final String KEY_BLINK_SLOW = "slow"; 76 | private static final String KEY_BLINK_FRENETIC = "frenetic"; 77 | 78 | private static final String PREF_NOTIFICATION_COUNTERS = "NotifCount"; 79 | private static final String PREF_NOTIFICATION_COUNT_FRIENDS = "nbFriends"; 80 | private static final String PREF_NOTIFICATION_COUNT_MESSAGES = "nbMessages"; 81 | private static final String PREF_NOTIFICATION_COUNT_GROUPS = "nbGroups"; 82 | private static final String PREF_NOTIFICATION_COUNT_NOTIFICATIONS = "nbNotifications"; 83 | private static final String CHANNEL_ID = "NOTIFICATIONS_CHANNEL"; 84 | 85 | private WindowManager windowManager; 86 | private WebView webview; 87 | private SharedPreferences sharedPreferences; 88 | 89 | private static WindowManager.LayoutParams getLayoutParams() { 90 | int LAYOUT_FLAG; 91 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 92 | LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; 93 | } else { 94 | LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE; 95 | } 96 | return new WindowManager.LayoutParams( 97 | WindowManager.LayoutParams.WRAP_CONTENT, 98 | WindowManager.LayoutParams.WRAP_CONTENT, 99 | LAYOUT_FLAG, 100 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 101 | | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 102 | | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, 103 | PixelFormat.TRANSLUCENT); 104 | } 105 | 106 | @SuppressLint("SetJavaScriptEnabled") 107 | @Override 108 | public void onCreate() { 109 | //TODO API 30->31: Foreground service launch restrictions 110 | // adb shell device_config put activity_manager default_fgs_starts_restriction_notification_enabled true 111 | 112 | //TODO API 30->31: Notification trampoline restrictions 113 | // adb shell dumpsys activity service com.android.systemui/.dump.SystemUIAuxiliaryDumpService 114 | super.onCreate(); 115 | 116 | Log.i("fbn", "Started notification check"); 117 | 118 | webview = new WebView(this); 119 | webview.setVisibility(View.GONE); 120 | webview.setLayoutParams(new ViewGroup.LayoutParams(0, 0)); 121 | webview.getSettings().setJavaScriptEnabled(true); 122 | webview.addJavascriptInterface(this, "customInterface"); 123 | webview.setWebViewClient(new WebViewClient() { 124 | @Override 125 | public void onPageFinished(WebView view, String url) { 126 | /* Copy-paste friendly version (for the '\'): 127 | javascript:get=function(url){elt=document.querySelector("[href*='/"+url+"']>strong"); return elt!=null ? elt.textContent.match(/[0-9]+/) : "null";}; 128 | var jsonString = window.location.pathname=="/login.php" ? '{"login":true}' : '{"login":false'+',"friends":'+get("friends")+',"messages":'+get("messages")+',"groups":'+get("groups")+',"notifications":'+get("notifications")+'}'; 129 | document.querySelector('[name=accept_only_essential]')?.click(); 130 | console.log(jsonString); 131 | console.log(document.body.innerHTML.substring(3900*0)); 132 | window.customInterface.processJSON(jsonString); 133 | if(window.location.pathname=="/") window.close(); 134 | */ 135 | webview.loadUrl("javascript:get=function(url){elt=document.querySelector(\"[href*='/\"+url+\"']>strong\"); return elt!=null ? elt.textContent.match(/[0-9]+/) : \"null\";};\n" + 136 | " var jsonString = window.location.pathname==\"/login.php\" ? '{\"login\":true}' : '{\"login\":false'+',\"friends\":'+get(\"friends\")+',\"messages\":'+get(\"messages\")+',\"groups\":'+get(\"groups\")+',\"notifications\":'+get(\"notifications\")+'}';\n" + 137 | " document.querySelector('[name=accept_only_essential]')?.click();\n" + 138 | " console.log(jsonString);\n" + 139 | " console.log(document.body.innerHTML.substring(3900*0));\n" + 140 | " window.customInterface.processJSON(jsonString);\n" + 141 | " if(window.location.pathname==\"/\") window.close();"); 142 | Log.i("fbn.UpdateService", "Loading finished"); 143 | } 144 | }); 145 | 146 | WebSettings webSettings = webview.getSettings(); 147 | webSettings.setBlockNetworkImage(true); 148 | webSettings.setUserAgentString(getString(R.string.app_name)); 149 | webview.loadUrl(URL_BOOKMARKS); 150 | 151 | WindowManager.LayoutParams params = getLayoutParams(); 152 | params.gravity = Gravity.NO_GRAVITY; 153 | params.x = 0; 154 | params.y = 0; 155 | params.width = 200; 156 | params.height = 200; 157 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 158 | if (Settings.canDrawOverlays(this)) { 159 | windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); 160 | windowManager.addView(webview, params); 161 | } else { 162 | windowManager = null; 163 | } 164 | } else { 165 | windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); 166 | windowManager.addView(webview, params); 167 | } 168 | sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); 169 | } 170 | 171 | private Notification.Builder getNewStyleNotification( 172 | int smallIcon, 173 | Bitmap largeIcon, 174 | String title, 175 | String text, 176 | int priority, 177 | Intent resultIntent, 178 | String soundURI, 179 | long[] vibrationPattern 180 | ) { 181 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 182 | CharSequence name = getString(R.string.R_string_notif_channel_name); 183 | String description = getString(R.string.notif_channel_description); 184 | int importance = NotificationManager.IMPORTANCE_DEFAULT; 185 | NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance); 186 | channel.setDescription(description); 187 | // Register the channel with the system; you can't change the importance 188 | // or other notification behaviors after this 189 | NotificationManager notificationManager = getSystemService(NotificationManager.class); 190 | notificationManager.createNotificationChannel(channel); 191 | } 192 | 193 | Notification.Builder mBuilder = 194 | new Notification.Builder(getApplicationContext()) 195 | .setSmallIcon(smallIcon) 196 | .setLargeIcon(largeIcon) 197 | .setContentTitle(title) 198 | .setContentText(text) 199 | .setPriority(priority) 200 | .setAutoCancel(true); 201 | 202 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 203 | mBuilder.setChannelId(CHANNEL_ID); 204 | } 205 | 206 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 207 | mBuilder.setCategory(Notification.CATEGORY_SOCIAL) 208 | .setVisibility(Notification.VISIBILITY_PRIVATE); 209 | } 210 | 211 | TaskStackBuilder stackBuilder = TaskStackBuilder.create(getApplicationContext()); 212 | stackBuilder.addNextIntent(resultIntent); 213 | PendingIntent resultPendingIntent = 214 | stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); 215 | mBuilder.setContentIntent(resultPendingIntent); 216 | 217 | if (soundURI != null) { 218 | Uri uri = Uri.parse(soundURI); 219 | mBuilder.setSound(uri); 220 | } 221 | 222 | mBuilder.setVibrate(vibrationPattern); 223 | 224 | return mBuilder; 225 | } 226 | 227 | 228 | @SuppressWarnings("unused") 229 | @JavascriptInterface 230 | public void processJSON(String jsonStr) { 231 | Log.i("fbn.UpdateService", jsonStr); 232 | try { 233 | JSONObject json = new JSONObject(jsonStr); 234 | NotificationManager mNotificationManager = 235 | (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 236 | if (json.getBoolean("login")) { 237 | Intent resultIntent = new Intent(getApplicationContext(), MainActivity.class); 238 | Notification.Builder notif = getNewStyleNotification(android.R.drawable.ic_dialog_alert, 239 | BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher), 240 | getString(R.string.could_not_get_notifications), 241 | getString(R.string.maybe_logged_out), 242 | Notification.PRIORITY_LOW, 243 | resultIntent, 244 | null, 245 | new long[]{}); 246 | mNotificationManager.notify(NOTIF_LOGIN, notif.build()); 247 | } else { 248 | // If we had a connection problem but now it's OK, remove the "login" notification 249 | mNotificationManager.cancel(NOTIF_LOGIN); 250 | int nbFriends = sharedPreferences.getBoolean(PREF_FRIEND_REQUESTS, true) ? json.optInt("friends", 0) : 0; 251 | int nbMessages = sharedPreferences.getBoolean(PREF_MESSAGES, true) ? json.optInt("messages", 0) : 0; 252 | int nbGroups = sharedPreferences.getBoolean(PREF_GROUPS, true) ? json.optInt("groups", 0) : 0; 253 | int nbNotifications = sharedPreferences.getBoolean(PREF_NOTIFICATIONS, true) ? json.optInt("notifications", 0) : 0; 254 | 255 | SharedPreferences settings = getSharedPreferences(PREF_NOTIFICATION_COUNTERS, 0); 256 | 257 | // If we have no notifications, remove the existing one 258 | Log.i("fbn.UpdateService", "F:" + nbFriends + " M:" + nbMessages + " G:" + nbGroups + " N:" + nbNotifications); 259 | 260 | if (nbFriends + nbMessages + nbGroups + nbNotifications == 0) { 261 | mNotificationManager.cancel(NOTIF_UNIFIED); 262 | } else if (nbFriends != settings.getInt(PREF_NOTIFICATION_COUNT_FRIENDS, 0) 263 | || nbMessages != settings.getInt(PREF_NOTIFICATION_COUNT_MESSAGES, 0) 264 | || nbGroups != settings.getInt(PREF_NOTIFICATION_COUNT_GROUPS, 0) 265 | || nbNotifications != settings.getInt(PREF_NOTIFICATION_COUNT_NOTIFICATIONS, 0)) { // If the count is the same as before, change nothing 266 | 267 | // Build a notification 268 | String notifText = getString(R.string.you_have); 269 | 270 | // If it is a multi-category notification, remove rephrase notification message 271 | boolean multipleCategories = false; 272 | if ((nbFriends > 0 ? 1 : 0) 273 | + (nbMessages > 0 ? 1 : 0) 274 | + (nbGroups > 0 ? 1 : 0) 275 | + (nbNotifications > 0 ? 1 : 0) > 1) { 276 | multipleCategories = true; 277 | notifText = ""; 278 | } 279 | 280 | // Build the message 281 | boolean first = true; 282 | if (nbFriends > 0) { 283 | first = false; 284 | notifText = notifText + " " + nbFriends + " " + 285 | (multipleCategories ? getString(R.string.new_friend_requests_short) : getString(R.string.new_friend_requests)); 286 | } 287 | if (nbMessages > 0) { 288 | if (!first) notifText = notifText + ","; 289 | first = false; 290 | notifText = notifText + " " + nbMessages + " " + 291 | (multipleCategories ? getString(R.string.new_messages_short) : getString(R.string.new_messages)); 292 | } 293 | if (nbGroups > 0) { 294 | if (!first) notifText = notifText + ","; 295 | first = false; 296 | notifText = notifText + " " + nbGroups + " " + 297 | (multipleCategories ? getString(R.string.new_messages_short) : getString(R.string.new_groups)); 298 | } 299 | if (nbNotifications > 0) { 300 | if (!first) notifText = notifText + ","; 301 | notifText = notifText + " " + nbNotifications + " " + 302 | (multipleCategories ? getString(R.string.new_notifications_short) : getString(R.string.new_notifications)); 303 | } 304 | 305 | // Choose the right sound and vibration style according to category weight (notification < friend < message) 306 | String soundURI = ""; 307 | long[] vibrationPattern = new long[]{0, 200}; 308 | int[] rate = new int[]{0, 0}; 309 | if (nbNotifications > 0) { 310 | soundURI = sharedPreferences.getString(PREF_SOUND_NOTIFICATIONS, null); 311 | vibrationPattern = getPattern(sharedPreferences.getString(PREF_VIBRATE_NOTIFICATIONS, KEY_VIBRATE_SHORT)); 312 | rate = getBlinkRate(Objects.requireNonNull(sharedPreferences.getString(PREF_BLINK_NOTIFICATIONS, ""))); 313 | } 314 | if (nbFriends > 0) { 315 | soundURI = sharedPreferences.getString(PREF_SOUND_FRIEND_REQUESTS, null); 316 | vibrationPattern = getPattern(sharedPreferences.getString(PREF_VIBRATE_FRIEND_REQUESTS, KEY_VIBRATE_SHORT)); 317 | rate = getBlinkRate(Objects.requireNonNull(sharedPreferences.getString(PREF_BLINK_FRIEND_REQUESTS, ""))); 318 | } 319 | if (nbMessages > 0) { 320 | soundURI = sharedPreferences.getString(PREF_SOUND_MESSAGES, null); 321 | vibrationPattern = getPattern(sharedPreferences.getString(PREF_VIBRATE_MESSAGES, KEY_VIBRATE_SHORT)); 322 | rate = getBlinkRate(Objects.requireNonNull(sharedPreferences.getString(PREF_BLINK_MESSAGES, ""))); 323 | } 324 | if (nbGroups > 0) { 325 | soundURI = sharedPreferences.getString(PREF_SOUND_GROUPS, null); 326 | vibrationPattern = getPattern(sharedPreferences.getString(PREF_VIBRATE_GROUPS, KEY_VIBRATE_SHORT)); 327 | rate = getBlinkRate(Objects.requireNonNull(sharedPreferences.getString(PREF_BLINK_GROUPS, ""))); 328 | } 329 | 330 | // Build the intent 331 | Intent resultIntent = new Intent(Intent.ACTION_VIEW); 332 | resultIntent.setData(Uri.parse(URL_HOME)); 333 | if (!multipleCategories) { 334 | // If only one category, make the notification more specific 335 | if (nbFriends > 0) { 336 | resultIntent.setData(Uri.parse(URL_FRIEND_REQUESTS)); 337 | } 338 | if (nbMessages > 0) { 339 | resultIntent.setData(Uri.parse(URL_MESSAGES)); 340 | } 341 | if (nbGroups > 0) { 342 | resultIntent.setData(Uri.parse(URL_GROUPS)); 343 | } 344 | if (nbNotifications > 0) { 345 | resultIntent.setData(Uri.parse(URL_NOTIFICATIONS)); 346 | } 347 | } 348 | 349 | // Basic common notification, will be altered for more important states (messages) 350 | Notification.Builder notif = getNewStyleNotification(R.drawable.ic_notification, 351 | BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher), 352 | getString(R.string.app_name), 353 | notifText, 354 | Notification.PRIORITY_DEFAULT, 355 | resultIntent, 356 | soundURI, 357 | vibrationPattern); 358 | 359 | // Set new priority and category if needed 360 | if (nbMessages > 0) { 361 | notif.setPriority(Notification.PRIORITY_HIGH); 362 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 363 | notif.setCategory(Notification.CATEGORY_MESSAGE); 364 | } 365 | } 366 | 367 | // Set blinking 368 | notif.setLights(Color.BLUE, rate[0], rate[1]); 369 | 370 | if (multipleCategories) { 371 | // If it's a multicategory notification, create the BigView, display in the same order as Facebook 372 | notif.setStyle(new Notification.BigTextStyle().bigText(notifText)); 373 | if (nbFriends > 0) { 374 | Intent btnIntent = (Intent) resultIntent.clone(); 375 | btnIntent.setData(Uri.parse(URL_FRIEND_REQUESTS)); 376 | TaskStackBuilder sBuilder = TaskStackBuilder.create(this); 377 | sBuilder.addNextIntent(btnIntent); 378 | PendingIntent pi = sBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); 379 | notif.addAction(R.drawable.ic_menu_invite, getString(R.string.friends), pi); 380 | } 381 | if (nbMessages > 0) { 382 | Intent btnIntent = (Intent) resultIntent.clone(); 383 | btnIntent.setData(Uri.parse(URL_MESSAGES)); 384 | TaskStackBuilder sBuilder = TaskStackBuilder.create(this); 385 | sBuilder.addNextIntent(btnIntent); 386 | PendingIntent pi = sBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); 387 | notif.addAction(R.drawable.ic_menu_start_conversation, getString(R.string.messages), pi); 388 | } 389 | if (nbGroups > 0) { 390 | Intent btnIntent = (Intent) resultIntent.clone(); 391 | btnIntent.setData(Uri.parse(URL_GROUPS)); 392 | TaskStackBuilder sBuilder = TaskStackBuilder.create(this); 393 | sBuilder.addNextIntent(btnIntent); 394 | PendingIntent pi = sBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); 395 | notif.addAction(R.drawable.ic_menu_groups, getString(R.string.messages), pi); 396 | } 397 | if (nbNotifications > 0) { 398 | Intent btnIntent = (Intent) resultIntent.clone(); 399 | btnIntent.setData(Uri.parse(URL_NOTIFICATIONS)); 400 | TaskStackBuilder sBuilder = TaskStackBuilder.create(this); 401 | sBuilder.addNextIntent(btnIntent); 402 | PendingIntent pi = sBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); 403 | notif.addAction(R.drawable.ic_menu_mapmode, getString(R.string.notifications), pi); 404 | } 405 | } 406 | 407 | mNotificationManager.notify(NOTIF_UNIFIED, notif.build()); 408 | } else { 409 | Log.i("fbn.UpdateService", "Same number of events per categories, skipping notification"); 410 | } 411 | 412 | // Save in the settings the current count of notifs per type 413 | SharedPreferences.Editor editor = settings.edit(); 414 | editor.putInt(PREF_NOTIFICATION_COUNT_FRIENDS, nbFriends); 415 | editor.putInt(PREF_NOTIFICATION_COUNT_MESSAGES, nbMessages); 416 | editor.putInt(PREF_NOTIFICATION_COUNT_GROUPS, nbGroups); 417 | editor.putInt(PREF_NOTIFICATION_COUNT_NOTIFICATIONS, nbNotifications); 418 | editor.apply(); 419 | } 420 | } catch (JSONException e) { 421 | e.printStackTrace(); 422 | } 423 | this.stopSelf(); 424 | } 425 | 426 | private int[] getBlinkRate(String pref) { 427 | switch (pref) { 428 | case KEY_BLINK_FRENETIC: 429 | return new int[]{300, 200}; 430 | case KEY_BLINK_NORMAL: 431 | return new int[]{1000, 4000}; 432 | case KEY_BLINK_SLOW: 433 | default: 434 | return new int[]{1000, 8000}; 435 | } 436 | } 437 | 438 | private long[] getPattern(String string) { 439 | switch (Objects.requireNonNull(sharedPreferences.getString(string, KEY_VIBRATE_SHORT))) { 440 | case KEY_VIBRATE_SHORT: 441 | return new long[]{0, 200}; 442 | case KEY_VIBRATE_LONG: 443 | return new long[]{0, 400}; 444 | case KEY_VIBRATE_DOUBLE: 445 | return new long[]{0, 200, 200, 200}; 446 | case KEY_VIBRATE_DOUBLE_LONG: 447 | return new long[]{0, 400, 300, 400}; 448 | default: 449 | return new long[0]; 450 | } 451 | } 452 | 453 | @Override 454 | public void onDestroy() { 455 | super.onDestroy(); 456 | if (webview != null && windowManager != null) 457 | windowManager.removeView(webview); 458 | } 459 | 460 | @Override 461 | public IBinder onBind(Intent intent) { 462 | return null; 463 | } 464 | 465 | } 466 | -------------------------------------------------------------------------------- /app/src/main/java/org/surrel/facebooknotifications/WakeupManager.java: -------------------------------------------------------------------------------- 1 | package org.surrel.facebooknotifications; 2 | 3 | import android.app.AlarmManager; 4 | import android.app.PendingIntent; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.SharedPreferences; 8 | import android.os.SystemClock; 9 | import android.preference.PreferenceManager; 10 | import android.util.Log; 11 | 12 | import java.util.Objects; 13 | 14 | public class WakeupManager { 15 | public static final String ENABLE_NOTIFICATION_SYNCHRO = "enable_notification_synchro"; 16 | public static final String UPDATE_INTERVAL = "update_interval"; 17 | public static final int DEFAULT_UPDATE_INTERVAL = 5; 18 | public static final int MIN_UPDATE_INTERVAL = 1; 19 | public static final int MAX_UPDATE_INTERVAL = 10080; 20 | 21 | public static void updateNotificationSystem(Context context) { 22 | Intent intentForService = new Intent(context, UpdateService.class); 23 | context.startService((Intent) intentForService.clone()); 24 | PendingIntent pendingIntent = PendingIntent.getService(context, 0, intentForService, PendingIntent.FLAG_IMMUTABLE); 25 | 26 | SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context); 27 | boolean notifications = sharedPref.getBoolean(ENABLE_NOTIFICATION_SYNCHRO, true); 28 | Log.i("fbn", "Notifications enabled? " + notifications); 29 | 30 | AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 31 | if (notifications) { 32 | int updateInterval = Integer.parseInt(Objects.requireNonNull(sharedPref.getString(UPDATE_INTERVAL, "15"))); 33 | long interval = (long) updateInterval * 1000 * 60; 34 | Log.i("fbn", "Notification interval: " + interval / 1000 + " s"); 35 | alarmManager.setInexactRepeating(MainActivity.AlarmType, SystemClock.elapsedRealtime() + 5000, interval, pendingIntent); 36 | } else { 37 | alarmManager.cancel(pendingIntent); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi-v11/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-hdpi-v11/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi-v9/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-hdpi-v9/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_menu_groups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-hdpi/ic_menu_groups.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_menu_invite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-hdpi/ic_menu_invite.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_menu_mapmode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-hdpi/ic_menu_mapmode.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_menu_start_conversation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-hdpi/ic_menu_start_conversation.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-hdpi/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi-v11/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-mdpi-v11/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi-v9/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-mdpi-v9/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_menu_groups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-mdpi/ic_menu_groups.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_menu_invite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-mdpi/ic_menu_invite.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_menu_mapmode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-mdpi/ic_menu_mapmode.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_menu_start_conversation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-mdpi/ic_menu_start_conversation.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-mdpi/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi-v11/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-xhdpi-v11/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi-v9/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-xhdpi-v9/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_menu_groups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-xhdpi/ic_menu_groups.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_menu_invite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-xhdpi/ic_menu_invite.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_menu_mapmode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-xhdpi/ic_menu_mapmode.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_menu_start_conversation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-xhdpi/ic_menu_start_conversation.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-xhdpi/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi-v11/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-xxhdpi-v11/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi-v9/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-xxhdpi-v9/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_menu_groups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-xxhdpi/ic_menu_groups.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_menu_invite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-xxhdpi/ic_menu_invite.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_menu_mapmode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-xxhdpi/ic_menu_mapmode.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_menu_start_conversation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-xxhdpi/ic_menu_start_conversation.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/drawable-xxhdpi/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Notifications pour FB 4 | 5 | Requête en cours 6 | Impossible de récupérer les notifications 7 | Vous être peut être déconnecté de Facebook 8 | Vous avez 9 | demande(s) d\'amitié 10 | nouveau(x) messages(s) 11 | nouvelle(s) activités sur les groupes. 12 | nouvelle(s) notification(s) 13 | demande(s) 14 | message(s) 15 | notifications(s) 16 | Amis 17 | Messages 18 | Groupes 19 | Notifications 20 | 21 | Préférences 22 | Activer la synchronisation 23 | Activer cette catégorie 24 | 25 | Son de notification 26 | Clignotement de la LED 27 | Vitesse de clignotement de la LED 28 | Fréquence de mise à jour 29 | Délai de mise à jour (en minutes) 30 | Vérifier les notifications environ toutes les … minutes, de une minute à une semaine 31 | Délai de mise à jour invalide, utilisation de la valeur par défaut: 32 | Délai de mise à jour trop court, utilisation de la valeur minimale: 33 | Délai de mise à jour trop long, utilisation de la valeur maximale: 34 | Style de vibration 35 | 36 | Sans vibration 37 | Vibration normale 38 | Vibration forte 39 | Double vibration 40 | Double vibration forte 41 | 42 | Lent 43 | Normal 44 | Frénétique 45 | 46 | Ouvrir dans un navigateur 47 | Quitter 48 | Problèmes ? 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @string/vibrate_none 5 | @string/vibrate_short 6 | @string/vibrate_long 7 | @string/vibrate_double 8 | @string/vibrate_double_long 9 | 10 | 12 | vibrate_none 13 | vibrate_short 14 | vibrate_long 15 | vibrate_double 16 | vibrate_double_long 17 | 18 | 19 | @string/blink_slow 20 | @string/blink_normal 21 | @string/blink_frenetic 22 | 23 | 25 | slow 26 | normal 27 | frenetic 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Notifications for FB 4 | 5 | Request pending 6 | Could not retrieve notifications 7 | Maybe you are logged out, please check 8 | You have 9 | friend request(s) 10 | new message(s) 11 | new group activity(es) 12 | new notifications(s) 13 | request(s) 14 | message(s) 15 | notifications(s) 16 | Friends 17 | Messages 18 | Groups 19 | Notifications 20 | 21 | Settings 22 | Enable synchronization 23 | Activate this category 24 | 25 | Choose notification sound 26 | LED blink rate 27 | Choose LED blink rate 28 | Update interval 29 | Approximate update time (in minutes) 30 | Check for notifications every … minutes, from one minute to one week 31 | Invalid update interval, using default value: 32 | Update interval too short, using minimum value: 33 | Update interval too long, using maximum value: 34 | Vibration style 35 | 36 | No vibration 37 | Normal vibration 38 | Strong vibration 39 | Double vibration 40 | Strong double vibration 41 | 42 | Slow 43 | Normal 44 | Frenetic 45 | 46 | Open in browser 47 | Quit 48 | Problems? 49 | 50 | Notification 51 | Notifications from Facebook 52 | 53 | -------------------------------------------------------------------------------- /app/src/main/res/xml/preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 18 | 19 | 23 | 27 | 35 | 43 | 51 | 52 | 53 | 57 | 61 | 69 | 77 | 85 | 86 | 87 | 91 | 95 | 103 | 111 | 119 | 120 | 121 | 125 | 129 | 137 | 145 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /app/src/test/java/org/surrel/facebooknotifications/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package org.surrel.facebooknotifications; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | google() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:8.3.1' 10 | 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | google() 19 | jcenter() 20 | } 21 | } 22 | 23 | task clean(type: Delete) { 24 | delete rootProject.buildDir 25 | } 26 | -------------------------------------------------------------------------------- /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: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 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 | android.nonFinalResIds=false 20 | android.nonTransitiveRClass=false 21 | android.useAndroidX=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsurrel/FacebookNotifications/aa17c479ffe10be52292bf354e767d351b149240/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------