├── .gitignore
├── LICENCE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── me
│ │ └── jakelane
│ │ └── wrapperforfacebook
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── ic_launcher-web.png
│ ├── java
│ │ └── me
│ │ │ └── jakelane
│ │ │ └── wrapperforfacebook
│ │ │ ├── CustomWebChromeClient.java
│ │ │ ├── Helpers.java
│ │ │ ├── JavaScriptHelpers.java
│ │ │ ├── JavaScriptInterfaces.java
│ │ │ ├── MainActivity.java
│ │ │ ├── NotificationService.java
│ │ │ ├── PollReceiver.java
│ │ │ ├── SettingsActivity.java
│ │ │ └── WebViewListener.java
│ └── res
│ │ ├── drawable
│ │ ├── ic_fab_checkin.xml
│ │ ├── ic_fab_menu.xml
│ │ ├── ic_fab_photo.xml
│ │ ├── ic_fab_text.xml
│ │ ├── ic_menu_back.xml
│ │ ├── ic_menu_fblogin.xml
│ │ ├── ic_menu_forward.xml
│ │ ├── ic_menu_friendreq.xml
│ │ ├── ic_menu_jump_top.xml
│ │ ├── ic_menu_mainmenu.xml
│ │ ├── ic_menu_messages.xml
│ │ ├── ic_menu_most_recent.xml
│ │ ├── ic_menu_news.xml
│ │ ├── ic_menu_notifications.xml
│ │ ├── ic_menu_notifications_active.xml
│ │ ├── ic_menu_refresh.xml
│ │ ├── ic_menu_search.xml
│ │ ├── ic_menu_settings.xml
│ │ ├── ic_menu_top_stories.xml
│ │ ├── notify_logo.xml
│ │ ├── side_nav_bar.xml
│ │ └── side_profile.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── app_bar_main.xml
│ │ ├── content_main.xml
│ │ ├── menu_badge_full.xml
│ │ ├── nav_header_main.xml
│ │ └── video_progress.xml
│ │ ├── menu
│ │ ├── activity_main_drawer.xml
│ │ └── 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-es
│ │ └── strings.xml
│ │ ├── values-it
│ │ └── strings.xml
│ │ ├── values-pt-rBR
│ │ └── strings.xml
│ │ ├── values-v21
│ │ └── styles.xml
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ ├── values-zh-rCN
│ │ └── strings.xml
│ │ ├── values-zh-rTW
│ │ └── strings.xml
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ │ └── xml
│ │ └── settings.xml
│ └── test
│ └── java
│ └── me
│ └── jakelane
│ └── wrapperforfacebook
│ └── ExampleUnitTest.java
├── assets
├── logo.svg
└── logo_outline.svg
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | #built application files
2 | *.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # generated files
12 | bin/
13 | gen/
14 |
15 | # Local configuration file (sdk path, etc)
16 | local.properties
17 |
18 | # Windows thumbnail db
19 | Thumbs.db
20 |
21 | # OSX files
22 | .DS_Store
23 |
24 | # Eclipse project files
25 | .classpath
26 | .project
27 |
28 | # Android Studio
29 | *.iml
30 | .idea
31 | #.idea/workspace.xml - remove # and delete .idea if it better suit your needs.
32 | .gradle
33 | build/
34 | .navigation/
35 |
36 | #NDK
37 | obj/
38 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Jake Lane
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 | # Toffeed [](http://unmaintained.tech/)
2 |
3 | Toffeed is a wrapper for Facebook's mobile site that provides enhancements to the normal browser experience.
4 |
5 | This repository is no longer maintained as I no longer have the time nor motivation for this project. There are a couple of forks around and [MaterialFBook by ZeeRooo](https://forum.xda-developers.com/android/apps-games/app-materialfbook-minimalist-facebook-t3477896) is a great one. Thank you so much to everyone who contributed to this project, it was an amazing learning experience for me and it helped kick my career off!
6 |
7 |
8 |
9 |
10 |
11 |
12 | ~~Please submit any bugs or feature requests on the [issue tracker](https://github.com/JakeLane/Toffeed/issues/new).~~
13 |
14 | ~~Any contributions to localisation are greatly appreciated. You can read how to contribute [here](https://github.com/JakeLane/Toffeed/wiki/Localisation)~~
15 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.3"
6 |
7 | lintOptions {
8 | checkReleaseBuilds false
9 | }
10 |
11 | defaultConfig {
12 | applicationId "me.jakelane.wrapperforfacebook"
13 | minSdkVersion 17
14 | targetSdkVersion 23
15 | versionCode 14
16 | versionName "1.5.5"
17 | }
18 | buildTypes {
19 | release {
20 | minifyEnabled true
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | }
25 |
26 | dependencies {
27 | testCompile 'junit:junit:4.12'
28 | compile 'com.android.support:appcompat-v7:23.4.0'
29 | compile 'com.android.support:design:23.4.0'
30 | compile 'com.facebook.android:facebook-android-sdk:4.6.0'
31 | compile('com.mikepenz:actionitembadge:3.1.9@aar') {
32 | transitive = true
33 | }
34 | compile 'com.squareup.picasso:picasso:2.5.2'
35 | compile 'de.hdodenhof:circleimageview:2.0.0'
36 | compile 'com.github.JakeLane:Android-AdvancedWebView:-SNAPSHOT'
37 | compile 'com.github.clans:fab:1.6.2'
38 | compile 'com.android.support:customtabs:23.4.0'
39 | compile 'com.github.ahorn:android-rss:master'
40 | compile 'org.jsoup:jsoup:1.8.3'
41 | compile 'com.greysonparrelli.permiso:permiso:0.1.3'
42 | }
43 |
--------------------------------------------------------------------------------
/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 /home/jake/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 |
19 | # Picasso
20 | -dontwarn com.squareup.okhttp.**
21 |
22 | # AdvancedWebView
23 | -keep class * extends android.webkit.WebChromeClient { *; }
24 | -dontwarn im.delight.android.webview.**
25 |
26 | # Local fun
27 | -keepclassmembers class * {
28 | @android.webkit.JavascriptInterface ;
29 | }
30 |
31 | -keeppackagenames org.jsoup.nodes
32 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/me/jakelane/wrapperforfacebook/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package me.jakelane.wrapperforfacebook;
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 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
20 |
23 |
24 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
49 |
52 |
55 |
58 |
61 |
64 |
67 |
70 |
71 |
72 |
73 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JakeLane/Toffeed/700f63e6f04265ae6d09fcf6c5174d5f86410429/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/app/src/main/java/me/jakelane/wrapperforfacebook/CustomWebChromeClient.java:
--------------------------------------------------------------------------------
1 | package me.jakelane.wrapperforfacebook;
2 |
3 | import android.view.LayoutInflater;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.webkit.WebChromeClient;
7 | import android.webkit.WebView;
8 | import android.widget.FrameLayout;
9 |
10 | import com.github.clans.fab.FloatingActionMenu;
11 |
12 |
13 | class CustomWebChromeClient extends WebChromeClient {
14 | private final MainActivity mActivity;
15 | private final WebView mWebView;
16 | private final ViewGroup mCustomViewContainer;
17 | private final FloatingActionMenu mMenuFAB;
18 | private View mVideoProgressView;
19 | private View mCustomView;
20 | private CustomViewCallback customViewCallback;
21 |
22 | public CustomWebChromeClient(MainActivity activity, WebView webview, FrameLayout viewcontainer) {
23 | mActivity = activity;
24 | mWebView = webview;
25 | mCustomViewContainer = viewcontainer;
26 | mMenuFAB = (FloatingActionMenu) activity.findViewById(R.id.menuFAB);
27 | }
28 |
29 | @Override
30 | public void onShowCustomView(View view, CustomViewCallback callback) {
31 | // if a view already exists then immediately terminate the new one
32 | if (mCustomView != null) {
33 | callback.onCustomViewHidden();
34 | return;
35 | }
36 |
37 | mCustomView = view;
38 | mWebView.setVisibility(View.GONE);
39 | mMenuFAB.hideMenuButton(true);
40 | mCustomViewContainer.setVisibility(View.VISIBLE);
41 | mCustomViewContainer.addView(view);
42 | customViewCallback = callback;
43 | }
44 |
45 | @Override
46 | public View getVideoLoadingProgressView() {
47 | if (mVideoProgressView == null) {
48 | LayoutInflater inflater = LayoutInflater.from(mActivity);
49 | mVideoProgressView = inflater.inflate(R.layout.video_progress, mWebView, false);
50 | }
51 | return mVideoProgressView;
52 | }
53 |
54 | @Override
55 | public void onHideCustomView() {
56 | super.onHideCustomView(); //To change body of overridden methods use File | Settings | File Templates.
57 | if (mCustomView == null) {
58 | return;
59 | }
60 |
61 | mWebView.setVisibility(View.VISIBLE);
62 | mMenuFAB.showMenuButton(true);
63 | mCustomViewContainer.setVisibility(View.GONE);
64 |
65 | // Hide the custom view.
66 | mCustomView.setVisibility(View.GONE);
67 |
68 | // Remove the custom view from its container.
69 | mCustomViewContainer.removeView(mCustomView);
70 | customViewCallback.onCustomViewHidden();
71 |
72 | mCustomView = null;
73 | }
74 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/jakelane/wrapperforfacebook/Helpers.java:
--------------------------------------------------------------------------------
1 | package me.jakelane.wrapperforfacebook;
2 |
3 | import android.app.Activity;
4 | import android.support.design.widget.Snackbar;
5 | import android.view.Menu;
6 | import android.view.View;
7 | import android.webkit.CookieManager;
8 |
9 | import com.facebook.login.LoginManager;
10 |
11 | import java.util.Arrays;
12 | import java.util.List;
13 |
14 | class Helpers {
15 | public static final String LogTag = "FBWrapper";
16 | static final List FB_PERMISSIONS = Arrays.asList("public_profile", "user_friends");
17 |
18 | // Method to retrieve a single cookie
19 | public static String getCookie() {
20 | CookieManager cookieManager = CookieManager.getInstance();
21 | String cookies = cookieManager.getCookie(MainActivity.FACEBOOK_URL_BASE);
22 | if (cookies != null) {
23 | String[] temp = cookies.split(";");
24 | for (String ar1 : temp) {
25 | if (ar1.contains("c_user")) {
26 | String[] temp1 = ar1.split("=");
27 | return temp1[1];
28 | }
29 | }
30 | }
31 | // Return null as we found no cookie
32 | return null;
33 | }
34 |
35 | // Prompt a login
36 | public static Snackbar loginPrompt(final View view) {
37 | final Snackbar snackBar = Snackbar.make(view, R.string.not_logged_in, Snackbar.LENGTH_INDEFINITE);
38 | snackBar.setAction(R.string.login_button, new View.OnClickListener() {
39 | @Override
40 | public void onClick(View v) {
41 | LoginManager.getInstance().logInWithReadPermissions((Activity) view.getContext(), FB_PERMISSIONS);
42 | }
43 | });
44 | snackBar.show();
45 | return snackBar;
46 | }
47 |
48 | // Uncheck all items menu
49 | public static void uncheckRadioMenu(Menu menu) {
50 | for (int i = 0; i < menu.size(); i++) {
51 | if (menu.getItem(i).isChecked()) {
52 | menu.getItem(i).setChecked(false);
53 | return;
54 | }
55 | }
56 | }
57 |
58 | public static boolean isInteger(String str) {
59 | return (str.matches("^-?\\d+$"));
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/main/java/me/jakelane/wrapperforfacebook/JavaScriptHelpers.java:
--------------------------------------------------------------------------------
1 | package me.jakelane.wrapperforfacebook;
2 |
3 | import android.net.UrlQuerySanitizer;
4 | import android.webkit.WebView;
5 |
6 | class JavaScriptHelpers {
7 | private static final int BADGE_UPDATE_INTERVAL = 15000;
8 |
9 | public static void updateCurrentTab(WebView view) {
10 | // Get the currently open tab and check on the navigation menu
11 | view.loadUrl("javascript:(function()%7Btry%7Bvar%20jewel%3Ddocument.querySelector(%22.popoverOpen%22).id%3B%22feed_jewel%22%3D%3Djewel%3Fdocument.querySelector('a%5Bhref*%3D%22%2Fhome.php%3Fsk%3Dh_nor%22%5D')%3Fandroid.getCurrent(%22most_recent%22)%3Aandroid.getCurrent(%22top_stories%22)%3Aandroid.getCurrent(jewel)%7Dcatch(_)%7Bandroid.getCurrent(%22null%22)%7D%7D)()");
12 | }
13 |
14 | public static void updateNums(WebView view) {
15 | view.loadUrl("javascript:(function()%7Bandroid.getNums(document.querySelector(%22%23notifications_jewel%20%3E%20a%20%3E%20div%20%3E%20span%5Bdata-sigil%3Dcount%5D%22).innerHTML%2Cdocument.querySelector(%22%23messages_jewel%20%3E%20a%20%3E%20div%20%3E%20span%5Bdata-sigil%3Dcount%5D%22).innerHTML%2Cdocument.querySelector(%22%23requests_jewel%20%3E%20a%20%3E%20div%20%3E%20span%5Bdata-sigil%3Dcount%5D%22).innerHTML)%7D)()");
16 | }
17 |
18 | public static void updateNumsService(WebView view) {
19 | view.loadUrl("javascript:(function()%7Bfunction%20n_s()%7Bandroid.getNums(document.querySelector(%22%23notifications_jewel%20%3E%20a%20%3E%20div%20%3E%20span%5Bdata-sigil%3Dcount%5D%22).innerHTML%2Cdocument.querySelector(%22%23messages_jewel%20%3E%20a%20%3E%20div%20%3E%20span%5Bdata-sigil%3Dcount%5D%22).innerHTML%2Cdocument.querySelector(%22%23requests_jewel%20%3E%20a%20%3E%20div%20%3E%20span%5Bdata-sigil%3Dcount%5D%22).innerHTML)%2CsetTimeout(n_s%2C" + BADGE_UPDATE_INTERVAL + ")%7Dtry%7Bn_s()%7Dcatch(_)%7B%7D%7D)()");
20 | }
21 |
22 | public static void paramLoader(WebView view, String url) {
23 | UrlQuerySanitizer sanitizer = new UrlQuerySanitizer();
24 | sanitizer.setAllowUnregisteredParamaters(true);
25 | sanitizer.parseUrl(url);
26 | String param = sanitizer.getValue("pageload");
27 | if (param != null) {
28 | switch (param) {
29 | case "composer":
30 | view.loadUrl("javascript:(function()%7Btry%7Bdocument.querySelector('button%5Bname%3D%22view_overview%22%5D').click()%7Dcatch(_)%7B%7D%7D)()");
31 | break;
32 | case "composer_photo":
33 | view.loadUrl("javascript:(function()%7Btry%7Bdocument.querySelector('button%5Bname%3D%22view_photo%22%5D').click()%7Dcatch(_)%7B%7D%7D)()");
34 | break;
35 | case "composer_checkin":
36 | view.loadUrl("javascript:(function()%7Btry%7Bdocument.querySelector('button%5Bname%3D%22view_location%22%5D').click()%7Dcatch(_)%7B%7D%7D)()");
37 | break;
38 | default:
39 | break;
40 | }
41 | }
42 |
43 | }
44 |
45 | public static void loadCSS(WebView view, String css) {
46 | // Inject CSS string to the HEAD of the webpage
47 | view.loadUrl("javascript:(function()%7Bvar%20styles%3Ddocument.createElement('style')%3Bstyles.innerHTML%3D'" + css + "'%2Cstyles.onload%3Dandroid.loadingCompleted()%2Cdocument.getElementsByTagName('head')%5B0%5D.appendChild(styles)%7D)()");
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/me/jakelane/wrapperforfacebook/JavaScriptInterfaces.java:
--------------------------------------------------------------------------------
1 | package me.jakelane.wrapperforfacebook;
2 |
3 | import android.content.SharedPreferences;
4 | import android.preference.PreferenceManager;
5 | import android.webkit.JavascriptInterface;
6 |
7 | @SuppressWarnings("unused")
8 | class JavaScriptInterfaces {
9 | private final MainActivity mContext;
10 | private final SharedPreferences mPreferences;
11 |
12 | // Instantiate the interface and set the context
13 | JavaScriptInterfaces(MainActivity c) {
14 | mContext = c;
15 | mPreferences = PreferenceManager.getDefaultSharedPreferences(c);
16 | }
17 |
18 | @JavascriptInterface
19 | public void loadingCompleted() {
20 | mContext.runOnUiThread(new Runnable() {
21 | @Override
22 | public void run() {
23 | mContext.setLoading(false);
24 | }
25 | });
26 | }
27 |
28 | @JavascriptInterface
29 | public void getCurrent(final String value) {
30 | mContext.runOnUiThread(new Runnable() {
31 | @Override
32 | public void run() {
33 | switch (value) {
34 | case "top_stories":
35 | if (mPreferences.getBoolean(SettingsActivity.KEY_PREF_MOST_RECENT_MENU, true)) {
36 | mContext.mNavigationView.setCheckedItem(R.id.nav_top_stories);
37 | } else {
38 | mContext.mNavigationView.setCheckedItem(R.id.nav_news);
39 | }
40 | break;
41 | case "most_recent":
42 | if (mPreferences.getBoolean(SettingsActivity.KEY_PREF_MOST_RECENT_MENU, true)) {
43 | mContext.mNavigationView.setCheckedItem(R.id.nav_most_recent);
44 | } else {
45 | mContext.mNavigationView.setCheckedItem(R.id.nav_news);
46 | }
47 | break;
48 | case "requests_jewel":
49 | mContext.mNavigationView.setCheckedItem(R.id.nav_friendreq);
50 | break;
51 | case "messages_jewel":
52 | mContext.mNavigationView.setCheckedItem(R.id.nav_messages);
53 | break;
54 | case "notifications_jewel":
55 | Helpers.uncheckRadioMenu(mContext.mNavigationView.getMenu());
56 | break;
57 | case "search_jewel":
58 | mContext.mNavigationView.setCheckedItem(R.id.nav_search);
59 | break;
60 | case "bookmarks_jewel":
61 | mContext.mNavigationView.setCheckedItem(R.id.nav_mainmenu);
62 | break;
63 | default:
64 | Helpers.uncheckRadioMenu(mContext.mNavigationView.getMenu());
65 | break;
66 | }
67 | }
68 | });
69 | }
70 |
71 | @JavascriptInterface
72 | public void getNums(final String notifications, final String messages, final String requests) {
73 | final int notifications_int = Helpers.isInteger(notifications) ? Integer.parseInt(notifications) : 0;
74 | final int messages_int = Helpers.isInteger(messages) ? Integer.parseInt(messages) : 0;
75 | final int requests_int = Helpers.isInteger(requests) ? Integer.parseInt(requests): 0;
76 | mContext.runOnUiThread(new Runnable() {
77 | @Override
78 | public void run() {
79 | mContext.setNotificationNum(notifications_int);
80 | mContext.setMessagesNum(messages_int);
81 | mContext.setRequestsNum(requests_int);
82 | }
83 | });
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/app/src/main/java/me/jakelane/wrapperforfacebook/MainActivity.java:
--------------------------------------------------------------------------------
1 | package me.jakelane.wrapperforfacebook;
2 |
3 | import android.Manifest;
4 | import android.content.Intent;
5 | import android.content.SharedPreferences;
6 | import android.graphics.Bitmap;
7 | import android.graphics.Color;
8 | import android.graphics.drawable.BitmapDrawable;
9 | import android.graphics.drawable.Drawable;
10 | import android.os.Bundle;
11 | import android.os.Handler;
12 | import android.preference.PreferenceManager;
13 | import android.support.annotation.NonNull;
14 | import android.support.design.widget.NavigationView;
15 | import android.support.design.widget.Snackbar;
16 | import android.support.v4.content.res.ResourcesCompat;
17 | import android.support.v4.view.GravityCompat;
18 | import android.support.v4.widget.DrawerLayout;
19 | import android.support.v4.widget.SwipeRefreshLayout;
20 | import android.support.v7.app.ActionBarDrawerToggle;
21 | import android.support.v7.app.AppCompatActivity;
22 | import android.support.v7.widget.Toolbar;
23 | import android.util.Log;
24 | import android.view.Menu;
25 | import android.view.MenuItem;
26 | import android.view.View;
27 | import android.webkit.URLUtil;
28 | import android.widget.FrameLayout;
29 | import android.widget.ImageView;
30 | import android.widget.TextView;
31 |
32 | import com.facebook.AccessToken;
33 | import com.facebook.CallbackManager;
34 | import com.facebook.FacebookCallback;
35 | import com.facebook.FacebookException;
36 | import com.facebook.FacebookSdk;
37 | import com.facebook.GraphRequest;
38 | import com.facebook.GraphResponse;
39 | import com.facebook.login.LoginBehavior;
40 | import com.facebook.login.LoginManager;
41 | import com.facebook.login.LoginResult;
42 | import com.github.clans.fab.FloatingActionMenu;
43 | import com.greysonparrelli.permiso.Permiso;
44 | import com.mikepenz.actionitembadge.library.ActionItemBadge;
45 | import com.mikepenz.actionitembadge.library.utils.BadgeStyle;
46 | import com.squareup.picasso.Picasso;
47 | import com.squareup.picasso.Target;
48 |
49 | import org.json.JSONException;
50 | import org.json.JSONObject;
51 |
52 | import java.io.UnsupportedEncodingException;
53 | import java.net.URLEncoder;
54 | import java.util.Arrays;
55 | import java.util.List;
56 |
57 | import im.delight.android.webview.AdvancedWebView;
58 |
59 | public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
60 | static final String FACEBOOK_URL_BASE = "https://m.facebook.com/";
61 | private static final String FACEBOOK_URL_BASE_ENCODED = "https%3A%2F%2Fm.facebook.com%2F";
62 | private static final List HOSTNAMES = Arrays.asList("facebook.com", "*.facebook.com", "*.fbcdn.net", "*.akamaihd.net");
63 | private final BadgeStyle BADGE_SIDE_FULL = new BadgeStyle(BadgeStyle.Style.LARGE, R.layout.menu_badge_full, R.color.colorAccent, R.color.colorAccent, Color.WHITE);
64 |
65 | // Members
66 | SwipeRefreshLayout swipeView;
67 | NavigationView mNavigationView;
68 | View mCoordinatorLayoutView;
69 | private FloatingActionMenu mMenuFAB;
70 | private AdvancedWebView mWebView;
71 | private final View.OnClickListener mFABClickListener = new View.OnClickListener() {
72 | @Override
73 | public void onClick(View v) {
74 | switch (v.getId()) {
75 | case R.id.textFAB:
76 | mWebView.loadUrl("javascript:(function()%7Btry%7Bdocument.querySelector('button%5Bname%3D%22view_overview%22%5D').click()%7Dcatch(_)%7Bwindow.location.href%3D%22" + FACEBOOK_URL_BASE_ENCODED + "%3Fpageload%3Dcomposer%22%7D%7D)()");
77 | break;
78 | case R.id.photoFAB:
79 | mWebView.loadUrl("javascript:(function()%7Btry%7Bdocument.querySelector('button%5Bname%3D%22view_photo%22%5D').click()%7Dcatch(_)%7Bwindow.location.href%3D%22" + FACEBOOK_URL_BASE_ENCODED + "%3Fpageload%3Dcomposer_photo%22%7D%7D)()");
80 | break;
81 | case R.id.checkinFAB:
82 | mWebView.loadUrl("javascript:(function()%7Btry%7Bdocument.querySelector('button%5Bname%3D%22view_location%22%5D').click()%7Dcatch(_)%7Bwindow.location.href%3D%22" + FACEBOOK_URL_BASE_ENCODED + "%3Fpageload%3Dcomposer_checkin%22%7D%7D)()");
83 | break;
84 | default:
85 | break;
86 | }
87 | mMenuFAB.close(true);
88 | }
89 | };
90 | private MenuItem mNotificationButton;
91 | private CallbackManager callbackManager;
92 | private Snackbar loginSnackbar = null;
93 | @SuppressWarnings("FieldCanBeLocal") // Will be garbage collected as a local variable
94 | private SharedPreferences.OnSharedPreferenceChangeListener listener;
95 | private boolean requiresReload = false;
96 | private String mUserLink = null;
97 | private SharedPreferences mPreferences;
98 |
99 | @Override
100 | protected void onCreate(Bundle savedInstanceState) {
101 | super.onCreate(savedInstanceState);
102 | FacebookSdk.sdkInitialize(this.getApplicationContext());
103 | setContentView(R.layout.activity_main);
104 | Permiso.getInstance().setActivity(this);
105 |
106 | // Preferences
107 | PreferenceManager.setDefaultValues(this, R.xml.settings, false);
108 | mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
109 | listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
110 | public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
111 | switch (key) {
112 | case SettingsActivity.KEY_PREF_JUMP_TOP_BUTTON:
113 | mNavigationView.getMenu().findItem(R.id.nav_jump_top).setVisible(prefs.getBoolean(key, false));
114 | break;
115 | case SettingsActivity.KEY_PREF_STOP_IMAGES:
116 | mWebView.getSettings().setBlockNetworkImage(prefs.getBoolean(key, false));
117 | requiresReload = true;
118 | break;
119 | case SettingsActivity.KEY_PREF_BACK_BUTTON:
120 | mNavigationView.getMenu().findItem(R.id.nav_back).setVisible(prefs.getBoolean(key, false));
121 | break;
122 | case SettingsActivity.KEY_PREF_MESSAGING:
123 | mNavigationView.getMenu().findItem(R.id.nav_messages).setVisible(prefs.getBoolean(key, false));
124 | break;
125 | case SettingsActivity.KEY_PREF_LOCATION:
126 | if (prefs.getBoolean(key, false)) {
127 | Permiso.getInstance().requestPermissions(new Permiso.IOnPermissionResult() {
128 | @Override
129 | public void onPermissionResult(Permiso.ResultSet resultSet) {
130 | if (resultSet.areAllPermissionsGranted()) {
131 | mWebView.setGeolocationEnabled(true);
132 | } else {
133 | Snackbar.make(mCoordinatorLayoutView, R.string.permission_denied, Snackbar.LENGTH_SHORT).show();
134 | }
135 | }
136 |
137 | @Override
138 | public void onRationaleRequested(Permiso.IOnRationaleProvided callback, String... permissions) {
139 | // TODO Permiso.getInstance().showRationaleInDialog("Title", "Message", null, callback);
140 | callback.onRationaleProvided();
141 | }
142 | }, Manifest.permission.ACCESS_FINE_LOCATION);
143 | }
144 | break;
145 | case SettingsActivity.KEY_PREF_MOST_RECENT_MENU:
146 | boolean most_recent = prefs.getBoolean(key, true);
147 | mNavigationView.getMenu().findItem(R.id.nav_news).setVisible(!most_recent);
148 | mNavigationView.getMenu().findItem(R.id.nav_top_stories).setVisible(most_recent);
149 | mNavigationView.getMenu().findItem(R.id.nav_most_recent).setVisible(most_recent);
150 | requiresReload = true;
151 | break;
152 | case SettingsActivity.KEY_PREF_FAB_SCROLL:
153 | mMenuFAB.showMenuButton(true);
154 | break;
155 | case SettingsActivity.KEY_PREF_HIDE_EDITOR:
156 | requiresReload = true;
157 | break;
158 | case SettingsActivity.KEY_PREF_HIDE_SPONSORED:
159 | requiresReload = true;
160 | break;
161 | case SettingsActivity.KEY_PREF_HIDE_BIRTHDAYS:
162 | requiresReload = true;
163 | break;
164 | case SettingsActivity.KEY_PREF_NOTIFICATIONS_ENABLED:
165 | PollReceiver.scheduleAlarms(getApplicationContext(), false);
166 | break;
167 | case SettingsActivity.KEY_PREF_NOTIFICATION_INTERVAL:
168 | PollReceiver.scheduleAlarms(getApplicationContext(), false);
169 | break;
170 | default:
171 | break;
172 | }
173 | }
174 | };
175 | mPreferences.registerOnSharedPreferenceChangeListener(listener);
176 |
177 | // Setup the toolbar
178 | Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar);
179 | setSupportActionBar(mToolbar);
180 |
181 | // Setup the DrawLayout
182 | final DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
183 | final ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, mToolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
184 | drawer.addDrawerListener(toggle);
185 | toggle.syncState();
186 |
187 | mNavigationView = (NavigationView) findViewById(R.id.nav_view);
188 | mNavigationView.setNavigationItemSelectedListener(this);
189 |
190 | // Create the badge for messages
191 | ActionItemBadge.update(this, mNavigationView.getMenu().findItem(R.id.nav_messages), (Drawable) null, BADGE_SIDE_FULL, Integer.MIN_VALUE);
192 | ActionItemBadge.update(this, mNavigationView.getMenu().findItem(R.id.nav_friendreq), (Drawable) null, BADGE_SIDE_FULL, Integer.MIN_VALUE);
193 |
194 | // Hide buttons if they are disabled
195 | if (!mPreferences.getBoolean(SettingsActivity.KEY_PREF_MESSAGING, false)) {
196 | mNavigationView.getMenu().findItem(R.id.nav_messages).setVisible(false);
197 | }
198 | if (!mPreferences.getBoolean(SettingsActivity.KEY_PREF_JUMP_TOP_BUTTON, false)) {
199 | mNavigationView.getMenu().findItem(R.id.nav_jump_top).setVisible(false);
200 | }
201 | if (!mPreferences.getBoolean(SettingsActivity.KEY_PREF_BACK_BUTTON, false)) {
202 | mNavigationView.getMenu().findItem(R.id.nav_back).setVisible(false);
203 | }
204 | boolean most_recent = mPreferences.getBoolean(SettingsActivity.KEY_PREF_MOST_RECENT_MENU, true);
205 | mNavigationView.getMenu().findItem(R.id.nav_news).setVisible(!most_recent);
206 | mNavigationView.getMenu().findItem(R.id.nav_top_stories).setVisible(most_recent);
207 | mNavigationView.getMenu().findItem(R.id.nav_most_recent).setVisible(most_recent);
208 |
209 | // Bind the Coordinator to member
210 | mCoordinatorLayoutView = findViewById(R.id.coordinatorLayout);
211 |
212 | // Start the Swipe to reload listener
213 | swipeView = (SwipeRefreshLayout) findViewById(R.id.swipeLayout);
214 | swipeView.setColorSchemeResources(R.color.colorPrimary);
215 | swipeView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
216 | @Override
217 | public void onRefresh() {
218 | mWebView.reload();
219 | }
220 | });
221 |
222 | // Inflate the FAB menu
223 | mMenuFAB = (FloatingActionMenu) findViewById(R.id.menuFAB);
224 | // Nasty hack to get the FAB menu button
225 | mMenuFAB.getChildAt(3).setOnLongClickListener(new View.OnLongClickListener() {
226 | @Override
227 | public boolean onLongClick(View v) {
228 | mMenuFAB.hideMenu(true);
229 | Handler handler = new Handler();
230 | handler.postDelayed(new Runnable() {
231 | @Override
232 | public void run() {
233 | // Show your View after 3 seconds
234 | mMenuFAB.showMenu(true);
235 | }
236 | }, 3000);
237 | return false;
238 | }
239 | });
240 | findViewById(R.id.textFAB).setOnClickListener(mFABClickListener);
241 | findViewById(R.id.photoFAB).setOnClickListener(mFABClickListener);
242 | findViewById(R.id.checkinFAB).setOnClickListener(mFABClickListener);
243 |
244 | // Load the WebView
245 | mWebView = (AdvancedWebView) findViewById(R.id.webview);
246 | assert mWebView != null;
247 | mWebView.addPermittedHostnames(HOSTNAMES);
248 | mWebView.setGeolocationEnabled(mPreferences.getBoolean(SettingsActivity.KEY_PREF_LOCATION, false));
249 |
250 | mWebView.setListener(this, new WebViewListener(this, mWebView));
251 | mWebView.addJavascriptInterface(new JavaScriptInterfaces(this), "android");
252 | registerForContextMenu(mWebView);
253 |
254 | mWebView.getSettings().setBlockNetworkImage(mPreferences.getBoolean(SettingsActivity.KEY_PREF_STOP_IMAGES, false));
255 | mWebView.getSettings().setAppCacheEnabled(true);
256 | mWebView.getSettings().setSupportZoom(true);
257 | mWebView.getSettings().setBuiltInZoomControls(false);
258 | mWebView.getSettings().setLoadWithOverviewMode(true);
259 | mWebView.getSettings().setUseWideViewPort(true);
260 | // Impersonate iPhone to prevent advertising garbage
261 | mWebView.getSettings().setUserAgentString("Mozilla/5.0 (Linux; Android 2.2; Nexus 5 Build/_BuildID_) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36");
262 |
263 | // Long press
264 | registerForContextMenu(mWebView);
265 | mWebView.setLongClickable(true);
266 | mWebView.setWebChromeClient(new CustomWebChromeClient(this, mWebView, (FrameLayout) findViewById(R.id.fullscreen_custom_content)));
267 |
268 | // Add OnClick listener to Profile picture
269 | ImageView profileImage = (ImageView) mNavigationView.getHeaderView(0).findViewById(R.id.profile_picture);
270 | profileImage.setClickable(true);
271 | profileImage.setOnClickListener(new View.OnClickListener() {
272 | @Override
273 | public void onClick(View v) {
274 | if (mUserLink != null) {
275 | drawer.closeDrawers();
276 | mWebView.loadUrl(mUserLink);
277 | }
278 | }
279 | });
280 |
281 | callbackManager = CallbackManager.Factory.create();
282 |
283 | FacebookCallback loginResult = new FacebookCallback() {
284 | @Override
285 | public void onSuccess(LoginResult loginResult) {
286 | mWebView.loadUrl(chooseUrl());
287 | updateUserInfo();
288 | }
289 |
290 | @Override
291 | public void onCancel() {
292 | checkLoggedInState();
293 | }
294 |
295 | @Override
296 | public void onError(FacebookException error) {
297 | Snackbar.make(mCoordinatorLayoutView, R.string.error_login, Snackbar.LENGTH_LONG).show();
298 | Log.e(Helpers.LogTag, error.toString());
299 | LoginManager.getInstance().logOut();
300 | checkLoggedInState();
301 | }
302 | };
303 |
304 | LoginManager.getInstance().setLoginBehavior(LoginBehavior.WEB_ONLY);
305 | LoginManager.getInstance().registerCallback(callbackManager, loginResult);
306 |
307 | if (checkLoggedInState()) {
308 | mWebView.loadUrl(chooseUrl());
309 | updateUserInfo();
310 | }
311 | }
312 |
313 | @Override
314 | protected void onResume() {
315 | super.onResume();
316 | mWebView.onResume();
317 | Permiso.getInstance().setActivity(this);
318 |
319 | // Check if we need to show a page reload snackbar
320 | if (requiresReload) {
321 | Snackbar reloadSnackbar = Snackbar.make(mCoordinatorLayoutView, R.string.hide_editor_newsfeed_snackbar, Snackbar.LENGTH_LONG);
322 | reloadSnackbar.setAction(R.string.menu_refresh, new View.OnClickListener() {
323 | @Override
324 | public void onClick(View v) {
325 | mWebView.reload();
326 | }
327 | });
328 | reloadSnackbar.show();
329 | requiresReload = false;
330 | }
331 | registerForContextMenu(mWebView);
332 | }
333 |
334 | @Override
335 | protected void onDestroy() {
336 | mWebView.onDestroy();
337 | super.onDestroy();
338 | }
339 |
340 | @Override
341 | protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
342 | super.onActivityResult(requestCode, resultCode, data);
343 | mWebView.onActivityResult(requestCode, resultCode, data);
344 | callbackManager.onActivityResult(requestCode, resultCode, data);
345 | }
346 |
347 | @Override
348 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
349 | super.onRequestPermissionsResult(requestCode, permissions, grantResults);
350 | Permiso.getInstance().onRequestPermissionResult(requestCode, permissions, grantResults);
351 | }
352 |
353 | @Override
354 | public void onBackPressed() {
355 | DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
356 | if (drawer.isDrawerOpen(GravityCompat.START)) {
357 | drawer.closeDrawer(GravityCompat.START);
358 | } else if (mWebView.canGoBack()) {
359 | mWebView.goBack();
360 | } else {
361 | super.onBackPressed();
362 | }
363 | }
364 |
365 | @Override
366 | public boolean onCreateOptionsMenu(Menu menu) {
367 | // Inflate the menu; this adds items to the action bar if it is present.
368 | getMenuInflater().inflate(R.menu.main, menu);
369 | mNotificationButton = menu.findItem(R.id.action_notifications);
370 |
371 | ActionItemBadge.update(this, mNotificationButton, ResourcesCompat.getDrawable(getResources(), R.drawable.ic_menu_notifications, null), ActionItemBadge.BadgeStyles.RED, Integer.MIN_VALUE);
372 | return true;
373 | }
374 |
375 | @Override
376 | public boolean onOptionsItemSelected(MenuItem item) {
377 | // Handle action bar item clicks here
378 | int id = item.getItemId();
379 | if (id == R.id.action_notifications) {
380 | // Load the notification page
381 | mWebView.loadUrl("javascript:(function()%7Btry%7Bdocument.querySelector('%23notifications_jewel%20%3E%20a').click()%7Dcatch(_)%7Bwindow.location.href%3D'" + FACEBOOK_URL_BASE_ENCODED + "notifications.php'%7D%7D)()");
382 | Helpers.uncheckRadioMenu(mNavigationView.getMenu());
383 | }
384 |
385 | // Update the notifications
386 | JavaScriptHelpers.updateNums(mWebView);
387 | return super.onOptionsItemSelected(item);
388 | }
389 |
390 | @Override
391 | public boolean onNavigationItemSelected(MenuItem item) {
392 | // Handle navigation view item clicks here.
393 | switch (item.getItemId()) {
394 | case R.id.nav_news:
395 | mWebView.loadUrl("javascript:(function()%7Btry%7Bdocument.querySelector('%23feed_jewel%20%3E%20a').click()%7Dcatch(_)%7Bwindow.location.href%3D'" + FACEBOOK_URL_BASE_ENCODED + "home.php'%7D%7D)()");
396 | item.setChecked(true);
397 | case R.id.nav_top_stories:
398 | mWebView.loadUrl("javascript:(function()%7Btry%7Bdocument.querySelector('a%5Bhref*%3D%22%2Fhome.php%3Fsk%3Dh_nor%22%5D').click()%7Dcatch(_)%7Bwindow.location.href%3D%22" + FACEBOOK_URL_BASE_ENCODED + "home.php%3Fsk%3Dh_nor%22%7D%7D)()");
399 | item.setChecked(true);
400 | break;
401 | case R.id.nav_most_recent:
402 | mWebView.loadUrl("javascript:(function()%7Btry%7Bdocument.querySelector('a%5Bhref*%3D%22%2Fhome.php%3Fsk%3Dh_chr%22%5D').click()%7Dcatch(_)%7Bwindow.location.href%3D%22" + FACEBOOK_URL_BASE_ENCODED + "home.php%3Fsk%3Dh_chr%22%7D%7D)()");
403 | item.setChecked(true);
404 | break;
405 | case R.id.nav_friendreq:
406 | mWebView.loadUrl("javascript:(function()%7Btry%7Bdocument.querySelector('%23requests_jewel%20%3E%20a').click()%7Dcatch(_)%7Bwindow.location.href%3D'" + FACEBOOK_URL_BASE_ENCODED + "friends%2Fcenter%2Frequests%2F'%7D%7D)()");
407 | item.setChecked(true);
408 | break;
409 | case R.id.nav_messages:
410 | mWebView.loadUrl("javascript:(function()%7Btry%7Bdocument.querySelector('%23messages_jewel%20%3E%20a').click()%7Dcatch(_)%7Bwindow.location.href%3D'" + FACEBOOK_URL_BASE_ENCODED + "messages%2F'%7D%7D)()");
411 | JavaScriptHelpers.updateNums(mWebView);
412 | item.setChecked(true);
413 | break;
414 | case R.id.nav_search:
415 | mWebView.loadUrl("javascript:(function()%7Btry%7Bdocument.querySelector('%23search_jewel%20%3E%20a').click()%7Dcatch(_)%7Bwindow.location.href%3D'" + FACEBOOK_URL_BASE_ENCODED + "search%2F'%7D%7D)()");
416 | item.setChecked(true);
417 | break;
418 | case R.id.nav_mainmenu:
419 | mWebView.loadUrl("javascript:(function()%7Btry%7Bdocument.querySelector('%23bookmarks_jewel%20%3E%20a').click()%7Dcatch(_)%7Bwindow.location.href%3D'" + FACEBOOK_URL_BASE_ENCODED + "home.php'%7D%7D)()");
420 | item.setChecked(true);
421 | break;
422 | case R.id.nav_fblogin:
423 | LoginManager.getInstance().logInWithReadPermissions(this, Helpers.FB_PERMISSIONS);
424 | break;
425 | case R.id.nav_jump_top:
426 | mWebView.scrollTo(0, 0);
427 | break;
428 | case R.id.nav_back:
429 | mWebView.goBack();
430 | break;
431 | case R.id.nav_reload:
432 | mWebView.reload();
433 | break;
434 | case R.id.nav_forward:
435 | mWebView.goForward();
436 | break;
437 | case R.id.nav_settings:
438 | Intent settingsActivity = new Intent(MainActivity.this, SettingsActivity.class);
439 | startActivity(settingsActivity);
440 | break;
441 | default:
442 | break;
443 | }
444 |
445 | DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
446 | drawer.closeDrawer(GravityCompat.START);
447 | return true;
448 | }
449 |
450 | public void setLoading(boolean loading) {
451 | // Toggle the WebView and Spinner visibility
452 | mWebView.setVisibility(loading ? View.GONE : View.VISIBLE);
453 | swipeView.setRefreshing(loading);
454 | }
455 |
456 | public boolean checkLoggedInState() {
457 | if (loginSnackbar != null) {
458 | loginSnackbar.dismiss();
459 | }
460 |
461 | if (AccessToken.getCurrentAccessToken() != null && Helpers.getCookie() != null) {
462 | // Logged in, show webview
463 | mWebView.setVisibility(View.VISIBLE);
464 |
465 | // Hide login button
466 | mNavigationView.getMenu().findItem(R.id.nav_fblogin).setVisible(false);
467 |
468 | // Enable navigation buttons
469 | mNavigationView.getMenu().setGroupEnabled(R.id.group_fbnav, true);
470 |
471 | // Start the Notification service (if not already running)
472 | PollReceiver.scheduleAlarms(getApplicationContext(), false);
473 | return true;
474 | } else {
475 | // Not logged in (possibly logged into Facebook OAuth and/or webapp)
476 | loginSnackbar = Helpers.loginPrompt(mCoordinatorLayoutView);
477 | setLoading(false);
478 | mWebView.setVisibility(View.GONE);
479 |
480 | // Show login button
481 | mNavigationView.getMenu().findItem(R.id.nav_fblogin).setVisible(true);
482 |
483 | // Disable navigation buttons
484 | mNavigationView.getMenu().setGroupEnabled(R.id.group_fbnav, false);
485 |
486 | // Cancel the Notification service if we are logged out
487 | PollReceiver.scheduleAlarms(getApplicationContext(), true);
488 |
489 | // Kill the Feed URL, so we don't get the wrong notifications
490 | mPreferences.edit().putString("feed_uri", null).apply();
491 | return false;
492 | }
493 | }
494 |
495 | private void updateUserInfo() {
496 | GraphRequest request = GraphRequest.newMeRequest(AccessToken.getCurrentAccessToken(), new GraphRequest.GraphJSONObjectCallback() {
497 | @Override
498 | public void onCompleted(JSONObject object, GraphResponse response) {
499 | // Update header
500 | try {
501 | String userID = object.getString("id");
502 | mUserLink = object.getString("link");
503 |
504 | // Set the user's name under the header
505 | ((TextView) findViewById(R.id.profile_name)).setText(object.getString("name"));
506 |
507 | // Set the cover photo with resizing
508 | Picasso.with(getApplicationContext()).load("https://graph.facebook.com/" + userID + "/picture?type=large").into((ImageView) findViewById(R.id.profile_picture));
509 |
510 | final View header = findViewById(R.id.header_layout);
511 | Picasso.with(getApplicationContext()).load(object.getJSONObject("cover").getString("source")).resize(header.getWidth(), header.getHeight()).centerCrop().error(R.drawable.side_nav_bar).into(new Target() {
512 | @Override
513 | public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
514 | Log.v(Helpers.LogTag, "Set cover photo");
515 | header.setBackground(new BitmapDrawable(getResources(), bitmap));
516 | }
517 |
518 | @Override
519 | public void onBitmapFailed(Drawable errorDrawable) {}
520 |
521 | @Override
522 | public void onPrepareLoad(Drawable placeHolderDrawable) {}
523 | });
524 | } catch (NullPointerException e) {
525 | Snackbar.make(mCoordinatorLayoutView, R.string.error_facebook_noconnection, Snackbar.LENGTH_LONG).show();
526 | } catch (JSONException e) {
527 | e.printStackTrace();
528 | Snackbar.make(mCoordinatorLayoutView, R.string.error_facebook_error, Snackbar.LENGTH_LONG).show();
529 | } catch (Exception e) {
530 | e.printStackTrace();
531 | Snackbar.make(mCoordinatorLayoutView, R.string.error_super_wrong, Snackbar.LENGTH_LONG).show();
532 | }
533 | }
534 | });
535 |
536 | Bundle parameters = new Bundle();
537 | parameters.putString("fields", "id,name,cover,link");
538 | request.setParameters(parameters);
539 | request.executeAsync();
540 | }
541 |
542 | public void setNotificationNum(int num) {
543 | if (num > 0) {
544 | ActionItemBadge.update(mNotificationButton, ResourcesCompat.getDrawable(getResources(), R.drawable.ic_menu_notifications_active, null), num);
545 | } else {
546 | // Hide the badge and show the washed-out button
547 | ActionItemBadge.update(mNotificationButton, ResourcesCompat.getDrawable(getResources(), R.drawable.ic_menu_notifications, null), Integer.MIN_VALUE);
548 | }
549 | }
550 |
551 | public void setMessagesNum(int num) {
552 | // Only update message count if enabled
553 | if (mPreferences.getBoolean(SettingsActivity.KEY_PREF_MESSAGING, false)) {
554 | if (num > 0) {
555 | ActionItemBadge.update(mNavigationView.getMenu().findItem(R.id.nav_messages), num);
556 | } else {
557 | // Hide the badge and show the washed-out button
558 | ActionItemBadge.update(mNavigationView.getMenu().findItem(R.id.nav_messages), Integer.MIN_VALUE);
559 | }
560 | }
561 | }
562 |
563 | public void setRequestsNum(int num) {
564 | if (num > 0) {
565 | ActionItemBadge.update(mNavigationView.getMenu().findItem(R.id.nav_friendreq), num);
566 | } else {
567 | // Hide the badge and show the washed-out button
568 | ActionItemBadge.update(mNavigationView.getMenu().findItem(R.id.nav_friendreq), Integer.MIN_VALUE);
569 | }
570 | }
571 |
572 | private String chooseUrl() {
573 | // Handle intents
574 | Intent intent = getIntent();
575 | String action = intent.getAction();
576 | String type = intent.getType();
577 |
578 | if (Intent.ACTION_SEND.equals(action) && type != null) {
579 | if (URLUtil.isValidUrl(intent.getStringExtra(Intent.EXTRA_TEXT))) {
580 | try {
581 | Log.v(Helpers.LogTag, "Shared URL Intent");
582 | return "https://mbasic.facebook.com/composer/?text=" + URLEncoder.encode(intent.getStringExtra(Intent.EXTRA_TEXT), "utf-8");
583 | } catch (UnsupportedEncodingException e) {
584 | e.printStackTrace();
585 | }
586 | }
587 | } else if (Intent.ACTION_VIEW.equals(action) && intent.getData() != null && URLUtil.isValidUrl(intent.getData().toString())) {
588 | // If there is a intent containing a facebook link, go there
589 | Log.v(Helpers.LogTag, "Opened URL Intent");
590 | return intent.getData().toString();
591 | }
592 |
593 | // If nothing has happened at this point, we want the default url
594 | return FACEBOOK_URL_BASE;
595 | }
596 | }
597 |
--------------------------------------------------------------------------------
/app/src/main/java/me/jakelane/wrapperforfacebook/NotificationService.java:
--------------------------------------------------------------------------------
1 | package me.jakelane.wrapperforfacebook;
2 |
3 | import android.app.IntentService;
4 | import android.app.Notification;
5 | import android.app.NotificationManager;
6 | import android.app.PendingIntent;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.content.SharedPreferences;
10 | import android.net.Uri;
11 | import android.os.Build;
12 | import android.preference.PreferenceManager;
13 | import android.support.v4.app.NotificationCompat;
14 | import android.support.v4.app.TaskStackBuilder;
15 | import android.support.v4.content.ContextCompat;
16 | import android.util.Log;
17 | import android.webkit.CookieManager;
18 |
19 | import org.jsoup.Jsoup;
20 | import org.jsoup.select.Elements;
21 | import org.mcsoxford.rss.RSSItem;
22 | import org.mcsoxford.rss.RSSReader;
23 | import org.mcsoxford.rss.RSSReaderException;
24 |
25 | import java.io.IOException;
26 | import java.text.DateFormat;
27 | import java.text.ParseException;
28 | import java.text.SimpleDateFormat;
29 | import java.util.ArrayList;
30 | import java.util.Date;
31 | import java.util.List;
32 | import java.util.Locale;
33 |
34 | public class NotificationService extends IntentService {
35 | private static final DateFormat DATEFORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH);
36 | private static final String NOTIFICATION_URL = "https://www.facebook.com/notifications";
37 | private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.4) Gecko/20100101 Firefox/4.0";
38 |
39 | private Date mLastNotification = null;
40 | private String feedURI = null;
41 | private SharedPreferences mPreferences;
42 |
43 | public NotificationService() {
44 | super("NotificationService");
45 | }
46 |
47 | @Override
48 | public void onCreate() {
49 | super.onCreate();
50 | mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
51 |
52 | // Try to find a feed uri
53 | feedURI = mPreferences.getString("feed_uri", null);
54 | Log.v(Helpers.LogTag, "Found Feed in preferences");
55 |
56 | // Try to find the most recent notification
57 | String dateString = mPreferences.getString("last_notification_date", null);
58 | if (dateString != null) {
59 | try {
60 | mLastNotification = DATEFORMAT.parse(dateString);
61 | } catch (ParseException e) {
62 | mLastNotification = null;
63 | Log.i(Helpers.LogTag, "Last notification timestamp could parsed");
64 | }
65 | }
66 | }
67 |
68 | @Override
69 | public void onDestroy() {
70 | super.onDestroy();
71 |
72 | // Save the most recent notification to preferences
73 | if (mLastNotification != null) {
74 | String datetime = DATEFORMAT.format(mLastNotification);
75 | mPreferences.edit().putString("last_notification_date", datetime).apply();
76 | }
77 | }
78 |
79 | @Override
80 | protected void onHandleIntent(Intent intent) {
81 | Log.v(Helpers.LogTag, "Notification alarm running");
82 |
83 | // Check if we have a Feed URL
84 | if (feedURI == null) {
85 | // Try to find the Feed URL
86 | updateFeed();
87 | }
88 |
89 | List notificationsBlob = fetchNotifications();
90 |
91 | // If we can't get the notifications, don't waste resources
92 | if (notificationsBlob == null) {
93 | return;
94 | }
95 |
96 | if (mLastNotification != null) {
97 | List unread = new ArrayList<>();
98 |
99 | for (RSSItem rssItem : notificationsBlob) {
100 | if (!rssItem.getPubDate().after(mLastNotification)) {
101 | // Only add notifications that have not been posted
102 | break;
103 | }
104 | unread.add(rssItem);
105 | }
106 |
107 | // Only proceed if there is a new notification
108 | if (unread.size() > 0) {
109 | // Update the last notification
110 | mLastNotification = unread.get(0).getPubDate();
111 |
112 | // Send the unread notifications to be posted
113 | sendNotification(unread);
114 | }
115 | } else {
116 | // No previous notification, set it to the most recent notification
117 | if (notificationsBlob.size() > 0) {
118 | mLastNotification = notificationsBlob.get(0).getPubDate();
119 | }
120 | }
121 | }
122 |
123 | private void updateFeed() {
124 | Log.i(Helpers.LogTag, "Updating Feed URL");
125 | try {
126 | Elements element = Jsoup.connect(NOTIFICATION_URL).userAgent(DESKTOP_USERAGENT).timeout(10000)
127 | .cookie(MainActivity.FACEBOOK_URL_BASE, CookieManager.getInstance().getCookie(MainActivity.FACEBOOK_URL_BASE)).get()
128 | .select("div#content").select("div.fwn").select("a[href*=rss20]");
129 | feedURI = "https://www.facebook.com/" + element.attr("href");
130 | mPreferences.edit().putString("feed_uri", feedURI).apply();
131 | Log.i(Helpers.LogTag, "Feed URL set");
132 | } catch (IOException e) {
133 | e.printStackTrace();
134 | Log.e(Helpers.LogTag, "Failed to find Feed URL");
135 | }
136 | }
137 |
138 | private List fetchNotifications() {
139 | RSSReader reader = new RSSReader();
140 | try {
141 | return reader.load(feedURI).getItems();
142 | } catch (RSSReaderException e) {
143 | Log.e(Helpers.LogTag, "Some error occurred with the RSS Reader");
144 | e.printStackTrace();
145 | } catch (Exception e) {
146 | Log.e(Helpers.LogTag, "Some error occurred when attempting to get RSS result");
147 | e.printStackTrace();
148 | }
149 | return null;
150 | }
151 |
152 | private void sendNotification(List notifications) {
153 | NotificationCompat.Builder mBuilder =
154 | new NotificationCompat.Builder(this)
155 | .setColor(ContextCompat.getColor(this, R.color.colorPrimary))
156 | .setSmallIcon(R.drawable.notify_logo)
157 | .setContentTitle(getString(R.string.app_name))
158 | .setAutoCancel(true)
159 | .setDefaults(-1);
160 |
161 | // Intent depends on context
162 | Intent resultIntent;
163 |
164 | if (notifications.size() > 1) {
165 | // If there are multiple notifications, mention the number
166 | String text = getString(R.string.notification_multiple_text, notifications.size());
167 | mBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text)).setContentText(text);
168 |
169 | // Set the url to the notification centre
170 | resultIntent = new Intent(this, MainActivity.class);
171 | resultIntent.setAction(Intent.ACTION_VIEW);
172 | resultIntent.setData(Uri.parse(MainActivity.FACEBOOK_URL_BASE + "notifications/"));
173 | } else {
174 | // Set the title
175 | RSSItem notification = notifications.get(0);
176 | mBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(notification.getTitle())).setContentText(notification.getTitle());
177 |
178 | // View all Notifications button
179 | Intent viewNotificationsIntent = new Intent(this, MainActivity.class);
180 | viewNotificationsIntent.setAction(Intent.ACTION_VIEW);
181 | viewNotificationsIntent.setData(Uri.parse(MainActivity.FACEBOOK_URL_BASE + "notifications/"));
182 | PendingIntent pendingViewNotifications = PendingIntent.getActivity(getApplicationContext(), 0, viewNotificationsIntent, 0);
183 | mBuilder.addAction(R.drawable.ic_menu_notifications_active, getString(R.string.notification_viewall), pendingViewNotifications);
184 |
185 | // Creates an explicit intent for an Activity in your app
186 | resultIntent = new Intent(this, MainActivity.class);
187 | resultIntent.setAction(Intent.ACTION_VIEW);
188 | resultIntent.setData(notification.getLink());
189 | }
190 |
191 | // Notification Priority (make LED blink)
192 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
193 | mBuilder.setPriority(Notification.PRIORITY_HIGH);
194 | }
195 |
196 | // Vibration
197 | if (mPreferences.getBoolean(SettingsActivity.KEY_PREF_NOTIFICATION_VIBRATE, true)) {
198 | mBuilder.setVibrate(new long[]{500, 500});
199 | } else {
200 | mBuilder.setVibrate(null);
201 | }
202 |
203 | // Create TaskStack to ensure correct back button behaviour
204 | TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
205 | // Adds the back stack for the Intent (but not the Intent itself)
206 | stackBuilder.addParentStack(MainActivity.class);
207 | // Adds the Intent that starts the Activity to the top of the stack
208 | stackBuilder.addNextIntent(resultIntent);
209 | PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
210 | mBuilder.setContentIntent(resultPendingIntent);
211 |
212 | // Build the notification
213 | Notification notification = mBuilder.build();
214 |
215 | // Set the LED colour
216 | notification.ledARGB = ContextCompat.getColor(this, R.color.colorPrimary);
217 |
218 | NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
219 | // notifyID allows you to update the notification later on.
220 | int notifyID = 1;
221 | mNotificationManager.notify(notifyID, notification);
222 |
223 | Log.i(Helpers.LogTag, "Notification posted");
224 | }
225 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/jakelane/wrapperforfacebook/PollReceiver.java:
--------------------------------------------------------------------------------
1 | package me.jakelane.wrapperforfacebook;
2 |
3 | import android.app.AlarmManager;
4 | import android.app.PendingIntent;
5 | import android.content.BroadcastReceiver;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.SharedPreferences;
9 | import android.preference.PreferenceManager;
10 | import android.util.Log;
11 |
12 | public class PollReceiver extends BroadcastReceiver {
13 | static void scheduleAlarms(Context ctxt, boolean cancel) {
14 | // Prepare the intent for the notification alarm
15 | AlarmManager mgr = (AlarmManager) ctxt.getSystemService(Context.ALARM_SERVICE);
16 | Intent i = new Intent(ctxt, NotificationService.class);
17 | PendingIntent pi = PendingIntent.getService(ctxt, 0, i, 0);
18 |
19 | // Start the alarm
20 | SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(ctxt);
21 | if (preferences.getBoolean(SettingsActivity.KEY_PREF_NOTIFICATIONS_ENABLED, false) && !cancel) {
22 | int interval = Integer.parseInt(preferences.getString(SettingsActivity.KEY_PREF_NOTIFICATION_INTERVAL, "600000"));
23 | mgr.setRepeating(AlarmManager.ELAPSED_REALTIME, 5000, interval, pi);
24 | Log.v(Helpers.LogTag, "Notification repeating alarm started");
25 | } else {
26 | // Cancel the alarm if notifications are disabled
27 | mgr.cancel(pi);
28 | Log.v(Helpers.LogTag, "Notification repeating alarm canceled");
29 | }
30 | }
31 |
32 | @Override
33 | public void onReceive(Context ctxt, Intent i) {
34 | scheduleAlarms(ctxt, false);
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/jakelane/wrapperforfacebook/SettingsActivity.java:
--------------------------------------------------------------------------------
1 | package me.jakelane.wrapperforfacebook;
2 |
3 | import android.os.Bundle;
4 | import android.preference.PreferenceFragment;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.MenuItem;
7 |
8 | public class SettingsActivity extends AppCompatActivity {
9 | public static final String KEY_PREF_BACK_BUTTON = "back_button_enabled";
10 | public static final String KEY_PREF_STOP_IMAGES = "stop_images";
11 | public static final String KEY_PREF_FAB_SCROLL = "hide_fab_on_scroll";
12 | public static final String KEY_PREF_MESSAGING = "messaging_enabled";
13 | public static final String KEY_PREF_JUMP_TOP_BUTTON = "jump_top_enabled";
14 | public static final String KEY_PREF_LOCATION = "location_enabled";
15 | public static final String KEY_PREF_MOST_RECENT_MENU = "most_recent_menu";
16 | public static final String KEY_PREF_HIDE_MENU_BAR = "hide_menu_bar";
17 | public static final String KEY_PREF_HIDE_EDITOR = "hide_editor_newsfeed";
18 | public static final String KEY_PREF_HIDE_SPONSORED = "hide_sponsored";
19 | public static final String KEY_PREF_HIDE_BIRTHDAYS = "hide_birthdays";
20 | public static final String KEY_PREF_NOTIFICATIONS_ENABLED = "notifications_enabled";
21 | public static final String KEY_PREF_NOTIFICATION_INTERVAL = "notification_interval";
22 | public static final String KEY_PREF_NOTIFICATION_VIBRATE = "notifications_vibration";
23 |
24 | @Override
25 | protected void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | assert getSupportActionBar() != null;
28 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
29 | getSupportActionBar().setTitle(R.string.menu_settings);
30 |
31 | // Display the fragment as the main content.
32 | getFragmentManager().beginTransaction().replace(android.R.id.content, new SettingsFragment()).commit();
33 | }
34 |
35 | @Override
36 | public boolean onOptionsItemSelected(MenuItem item) {
37 | switch (item.getItemId()) {
38 | case android.R.id.home:
39 | finish();
40 | return true;
41 | default:
42 | return super.onOptionsItemSelected(item);
43 | }
44 | }
45 |
46 | public static class SettingsFragment extends PreferenceFragment {
47 | @Override
48 | public void onCreate(Bundle savedInstanceState) {
49 | super.onCreate(savedInstanceState);
50 |
51 | // Load the preferences from an XML resource
52 | addPreferencesFromResource(R.xml.settings);
53 | }
54 | }
55 | }
56 |
57 |
--------------------------------------------------------------------------------
/app/src/main/java/me/jakelane/wrapperforfacebook/WebViewListener.java:
--------------------------------------------------------------------------------
1 | package me.jakelane.wrapperforfacebook;
2 |
3 | import android.Manifest;
4 | import android.app.DownloadManager;
5 | import android.app.PendingIntent;
6 | import android.content.ClipData;
7 | import android.content.ClipboardManager;
8 | import android.content.Context;
9 | import android.content.Intent;
10 | import android.content.SharedPreferences;
11 | import android.graphics.Bitmap;
12 | import android.graphics.drawable.Drawable;
13 | import android.net.Uri;
14 | import android.net.UrlQuerySanitizer;
15 | import android.os.Environment;
16 | import android.preference.PreferenceManager;
17 | import android.provider.MediaStore;
18 | import android.support.customtabs.CustomTabsIntent;
19 | import android.support.design.widget.Snackbar;
20 | import android.support.v4.content.ContextCompat;
21 | import android.util.Log;
22 | import android.view.ContextMenu;
23 | import android.view.MenuItem;
24 | import android.view.View;
25 | import android.webkit.WebView;
26 |
27 | import com.github.clans.fab.FloatingActionMenu;
28 | import com.greysonparrelli.permiso.Permiso;
29 | import com.squareup.picasso.Picasso;
30 | import com.squareup.picasso.Target;
31 |
32 | import java.io.File;
33 |
34 | import im.delight.android.webview.AdvancedWebView;
35 |
36 | class WebViewListener implements AdvancedWebView.Listener {
37 | private static final int ID_SAVE_IMAGE = 0;
38 | private static final int ID_SHARE_IMAGE = 1;
39 | private static final int ID_COPY_IMAGE_LINK = 2;
40 | private static final int ID_SHARE_LINK = 3;
41 | private static final int ID_COPY_LINK = 4;
42 |
43 | // *{-webkit-tap-highlight-color: rgba(0,0,0, 0.0);outline: none;}
44 | private static final String HIDE_ORANGE_FOCUS = "*%7B-webkit-tap-highlight-color%3Atransparent%3Boutline%3A0%7D";
45 | // #page{top:-45px;}
46 | private static final String HIDE_MENU_BAR_CSS = "%23page%7Btop%3A-45px%7D";
47 | // #mbasic_inline_feed_composer{display:none}
48 | private static final String HIDE_COMPOSER_CSS = "%23mbasic_inline_feed_composer%7Bdisplay%3Anone%7D";
49 | // article[data-ft*=ei]{display:none;}
50 | private static final String HIDE_SPONSORED = "article%5Bdata-ft*%3Dei%5D%7Bdisplay%3Anone%7D";
51 | // article#u_1j_4{display:none;}
52 | private static final String HIDE_BIRTHDAYS = "article%23u_1j_4%7Bdisplay%3Anone%3B%7D";
53 | // ._59e9._55wr._4g33._400s{display:none}
54 | private static final String HIDE_TOP_STORIES_BUTTON = "._59e9._55wr._4g33._400s%7Bdisplay%3Anone%7D";
55 |
56 | private final MainActivity mActivity;
57 | private final SharedPreferences mPreferences;
58 | private final AdvancedWebView mWebView;
59 | private final FloatingActionMenu mMenuFAB;
60 | private final DownloadManager mDownloadManager;
61 |
62 | private final int mScrollThreshold;
63 | private final View mCoordinatorLayoutView;
64 |
65 | WebViewListener(MainActivity activity, WebView view) {
66 | mActivity = activity;
67 | mCoordinatorLayoutView = activity.mCoordinatorLayoutView;
68 | mWebView = (AdvancedWebView) view;
69 | mPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
70 | mScrollThreshold = activity.getResources().getDimensionPixelOffset(R.dimen.fab_scroll_threshold);
71 | mMenuFAB = (FloatingActionMenu) activity.findViewById(R.id.menuFAB);
72 | mDownloadManager = (DownloadManager) mActivity.getSystemService(Context.DOWNLOAD_SERVICE);
73 | }
74 |
75 | @Override
76 | public void onPageStarted(String url, Bitmap favicon) {
77 | // Show the spinner and hide the WebView
78 | mActivity.setLoading(true);
79 | }
80 |
81 | @Override
82 | public void onPageFinished(String url) {
83 | // Only do things if logged in
84 | if (mActivity.checkLoggedInState()) {
85 | // Load a certain page if there is a parameter
86 | JavaScriptHelpers.paramLoader(mWebView, url);
87 |
88 | // Hide Orange highlight on focus
89 | String css = HIDE_ORANGE_FOCUS;
90 |
91 | // Hide the menu bar (but not on the composer or if disabled)
92 | if (mPreferences.getBoolean(SettingsActivity.KEY_PREF_HIDE_MENU_BAR, true) && !url.contains("/composer/") && !url.contains("/friends/")) {
93 | css += HIDE_MENU_BAR_CSS;
94 | mActivity.swipeView.setEnabled(true);
95 | } else {
96 | mActivity.swipeView.setEnabled(false);
97 | }
98 |
99 | if (url.contains("mbasic.facebook.com/composer/?text=")) {
100 | UrlQuerySanitizer sanitizer = new UrlQuerySanitizer();
101 | sanitizer.setAllowUnregisteredParamaters(true);
102 | sanitizer.parseUrl(url);
103 | String param = sanitizer.getValue("text");
104 |
105 | mWebView.loadUrl("javascript:(function()%7Bdocument.querySelector('%23composerInput').innerHTML%3D'" + param + "'%7D)()");
106 | }
107 |
108 | // Hide the status editor on the News Feed if setting is enabled
109 | if (mPreferences.getBoolean(SettingsActivity.KEY_PREF_HIDE_EDITOR, true)) {
110 | css += HIDE_COMPOSER_CSS;
111 | }
112 |
113 | // Hide 'Sponsored' content (ads)
114 | if (mPreferences.getBoolean(SettingsActivity.KEY_PREF_HIDE_SPONSORED, true)) {
115 | css += HIDE_SPONSORED;
116 | }
117 |
118 | // Hide birthday content from News Feed
119 | if (mPreferences.getBoolean(SettingsActivity.KEY_PREF_HIDE_BIRTHDAYS, true)) {
120 | css += HIDE_BIRTHDAYS;
121 | }
122 |
123 | // Hide Top Stories button on News Feed
124 | if (mPreferences.getBoolean(SettingsActivity.KEY_PREF_MOST_RECENT_MENU, true)) {
125 | css += HIDE_TOP_STORIES_BUTTON;
126 | }
127 |
128 | // Inject the css
129 | JavaScriptHelpers.loadCSS(mWebView, css);
130 |
131 | // Get the currently open tab and check on the navigation menu
132 | JavaScriptHelpers.updateCurrentTab(mWebView);
133 |
134 | // Get the notification number
135 | JavaScriptHelpers.updateNumsService(mWebView);
136 |
137 | // Stop loading
138 | mActivity.setLoading(false);
139 | }
140 | }
141 |
142 | @Override
143 | public void onPageError(int errorCode, String description, String failingUrl) {
144 | mActivity.setLoading(false);
145 | }
146 |
147 | @Override
148 | public void onDownloadRequested(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
149 | }
150 |
151 | @Override
152 | public void onExternalPageRequest(String url) {
153 | Log.i(Helpers.LogTag, "External page: " + url);
154 |
155 | // Launch another Activity that handles URLs
156 | CustomTabsIntent.Builder intentBuilder = new CustomTabsIntent.Builder();
157 | intentBuilder.setShowTitle(true);
158 | intentBuilder.setToolbarColor(ContextCompat.getColor(mActivity, R.color.colorPrimary));
159 |
160 | Intent actionIntent = new Intent(Intent.ACTION_SEND);
161 | actionIntent.setType("text/plain");
162 | actionIntent.putExtra(Intent.EXTRA_TEXT, url);
163 |
164 | PendingIntent menuItemPendingIntent = PendingIntent.getActivity(mActivity, 0, actionIntent, 0);
165 | intentBuilder.addMenuItem(mActivity.getString(R.string.share_text), menuItemPendingIntent);
166 | try {
167 | intentBuilder.build().launchUrl(mActivity, Uri.parse(url));
168 | } catch (android.content.ActivityNotFoundException ex) {
169 | Log.e(Helpers.LogTag, "Could not launch url, activity was not found");
170 | }
171 | }
172 |
173 | @Override
174 | public void onScrollChange(int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
175 | // Make sure the hiding is enabled and the scroll was significant
176 | if (mPreferences.getBoolean(SettingsActivity.KEY_PREF_FAB_SCROLL, false) && Math.abs(oldScrollY - scrollY) > mScrollThreshold) {
177 | if (scrollY > oldScrollY) {
178 | // User scrolled down, hide the button
179 | mMenuFAB.hideMenuButton(true);
180 | } else if (scrollY < oldScrollY) {
181 | // User scrolled up, show the button
182 | mMenuFAB.showMenuButton(true);
183 | }
184 | }
185 | }
186 |
187 | @Override
188 | public void onCreateContextMenu(ContextMenu contextMenu) {
189 | final WebView.HitTestResult result = mWebView.getHitTestResult();
190 |
191 | MenuItem.OnMenuItemClickListener handler = new MenuItem.OnMenuItemClickListener() {
192 | public boolean onMenuItemClick(MenuItem item) {
193 | int i = item.getItemId();
194 | if (i == ID_SAVE_IMAGE) {
195 | Permiso.getInstance().requestPermissions(new Permiso.IOnPermissionResult() {
196 | @Override
197 | public void onPermissionResult(Permiso.ResultSet resultSet) {
198 | if (resultSet.areAllPermissionsGranted()) {
199 | // Save the image
200 | Uri uri = Uri.parse(result.getExtra());
201 | DownloadManager.Request request = new DownloadManager.Request(uri);
202 |
203 | // Set the download directory
204 | File downloads_dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
205 | if (!downloads_dir.exists()) {
206 | if (!downloads_dir.mkdirs()) {
207 | return;
208 | }
209 | }
210 | File destinationFile = new File(downloads_dir, uri.getLastPathSegment());
211 | request.setDestinationUri(Uri.fromFile(destinationFile));
212 |
213 | // Make notification stay after download
214 | request.setVisibleInDownloadsUi(true);
215 | request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
216 |
217 | // Start the download
218 | mDownloadManager.enqueue(request);
219 | } else {
220 | Snackbar.make(mCoordinatorLayoutView, R.string.permission_denied, Snackbar.LENGTH_SHORT).show();
221 | }
222 | }
223 |
224 | @Override
225 | public void onRationaleRequested(Permiso.IOnRationaleProvided callback, String... permissions) {
226 | // TODO Permiso.getInstance().showRationaleInDialog("Title", "Message", null, callback);
227 | callback.onRationaleProvided();
228 | }
229 | }, Manifest.permission.WRITE_EXTERNAL_STORAGE);
230 | return true;
231 | } else if (i == ID_SHARE_IMAGE) {
232 | final Uri uri = Uri.parse(result.getExtra());
233 | // Share image
234 | Target target = new Target() {
235 | @Override
236 | public void onPrepareLoad(Drawable placeHolderDrawable) {
237 | }
238 |
239 | @Override
240 | public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom arg1) {
241 | String path = MediaStore.Images.Media.insertImage(mActivity.getContentResolver(), bitmap, uri.getLastPathSegment(), null);
242 |
243 | Intent shareIntent = new Intent(Intent.ACTION_SEND);
244 | shareIntent.setType("image/*");
245 | shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(path));
246 | mActivity.startActivity(Intent.createChooser(shareIntent, mActivity.getString(R.string.context_share_image)));
247 | }
248 |
249 | @Override
250 | public void onBitmapFailed(Drawable errorDrawable) {
251 | }
252 | };
253 |
254 | Picasso.with(mActivity).load(uri).into(target);
255 | Snackbar.make(mCoordinatorLayoutView, R.string.context_share_image_progress, Snackbar.LENGTH_SHORT).show();
256 | return true;
257 | } else if (i == ID_COPY_IMAGE_LINK || i == ID_COPY_LINK) {
258 | // Copy the image link to the clipboard
259 | ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE);
260 | ClipData clip = ClipData.newUri(mActivity.getContentResolver(), "URI", Uri.parse(result.getExtra()));
261 | clipboard.setPrimaryClip(clip);
262 | Snackbar.make(mCoordinatorLayoutView, R.string.content_copy_link_done, Snackbar.LENGTH_LONG).show();
263 | return true;
264 | } else if (i == ID_SHARE_LINK) {
265 | // Share the link
266 | Intent shareIntent = new Intent(Intent.ACTION_SEND);
267 | shareIntent.setType("text/plain");
268 | shareIntent.putExtra(Intent.EXTRA_TEXT, result.getExtra());
269 | mActivity.startActivity(Intent.createChooser(shareIntent, mActivity.getString(R.string.context_share_link)));
270 | return true;
271 | } else {
272 | return false;
273 | }
274 | }
275 | };
276 |
277 | // Long pressed image
278 | if (result.getType() == WebView.HitTestResult.IMAGE_TYPE || result.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
279 | contextMenu.add(0, ID_SAVE_IMAGE, 0, R.string.context_save_image).setOnMenuItemClickListener(handler);
280 | contextMenu.add(0, ID_SHARE_IMAGE, 0, R.string.context_share_image).setOnMenuItemClickListener(handler);
281 | contextMenu.add(0, ID_COPY_IMAGE_LINK, 0, R.string.context_copy_image_link).setOnMenuItemClickListener(handler);
282 | }
283 |
284 | // Long pressed link
285 | if (result.getType() == WebView.HitTestResult.SRC_ANCHOR_TYPE || result.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
286 | contextMenu.add(0, ID_SHARE_LINK, 0, R.string.context_share_link).setOnMenuItemClickListener(handler);
287 | contextMenu.add(0, ID_COPY_LINK, 0, R.string.context_copy_link).setOnMenuItemClickListener(handler);
288 | }
289 | }
290 | }
291 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_fab_checkin.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_fab_menu.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_fab_photo.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_fab_text.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_back.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_fblogin.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_forward.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_friendreq.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_jump_top.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_mainmenu.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_messages.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_most_recent.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_news.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_notifications.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_notifications_active.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_refresh.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_search.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_settings.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_top_stories.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/notify_logo.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/side_nav_bar.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/side_profile.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
16 |
17 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/app_bar_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
17 |
24 |
25 |
26 |
27 |
28 |
29 |
36 |
37 |
44 |
45 |
52 |
53 |
60 |
61 |
62 |
63 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/menu_badge_full.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/nav_header_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
24 |
25 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/video_progress.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/activity_main_drawer.xml:
--------------------------------------------------------------------------------
1 |
2 |
63 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JakeLane/Toffeed/700f63e6f04265ae6d09fcf6c5174d5f86410429/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JakeLane/Toffeed/700f63e6f04265ae6d09fcf6c5174d5f86410429/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JakeLane/Toffeed/700f63e6f04265ae6d09fcf6c5174d5f86410429/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JakeLane/Toffeed/700f63e6f04265ae6d09fcf6c5174d5f86410429/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JakeLane/Toffeed/700f63e6f04265ae6d09fcf6c5174d5f86410429/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-es/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Toffeed
4 |
5 | Abrir cajón de navegación
6 | Cerrar cajón de navegación
7 |
8 | Usuario desconocido
9 | Imágen de perfil
10 |
11 | Notificaciones
12 | Noticias
13 | Solicitudes de amistad
14 | Mensajes
15 | Búsqueda
16 | Menú Principal
17 |
18 | Entrar a Facebook
19 | Ir a arriba
20 | Recargar
21 | Atrás
22 | Adelante
23 | Opciones
24 |
25 | Publicar tu estado
26 | Publicar una foto
27 | Registrar visita
28 | Guardar imágen
29 |
30 | Compartir imágen
31 | Copiar enlace de imágen
32 | Compartir enlace
33 | Copiar enlace
34 |
35 | Compartiendo imágen…
36 | Enlace copiado al portapapeles
37 |
38 | Notificaciones
39 |
40 | Notificaciones en segundo plano
41 | Al activarlo, recibirás las notificaciones de Facebook.
42 |
43 | Intervalo de notificaciones en segundo plano
44 | Especifica el intervalo de actualización del servicio de notificación.
45 |
46 | - 5 minutos
47 | - 10 minutos
48 | - 15 minutos
49 | - 30 minutos
50 | - 1 hora
51 | - 2 horas
52 |
53 |
54 | - 300000
55 | - 600000
56 | - 900000
57 | - 1800000
58 | - 3600000
59 | - 7200000
60 |
61 |
62 | Vibrar
63 | Al activarlo, las notificaciones harán vibrar tu dispositivo.
64 |
65 | Contenido
66 |
67 | Acceso a tu ubicación
68 | Activa esto para permitir a Facebook obtener tu ubicación.
69 |
70 | Ocultar editor de estado en las noticias
71 | Al activarlo, el editor de estado no se verá en las noticias.
72 | Recargar para activar los cambios
73 |
74 | No cargar imágenes
75 | Al activarlo, no se descargarán imágenes. Algunas imágenes podrian permanecer en la caché.
76 |
77 | No mostrar contenido "patrocinado"
78 | Al activarlo, se ocultará el contenido "patrocinado"(anuncios) de la noticias.
79 |
80 | Navegación
81 |
82 | Ocultar el botón flotante de acción al deslizar
83 | Ocultar el botón cuando se desliza hacia abajo
84 |
85 | Mensajes
86 | Al activarlo, tendrás acceso a los mensajes de Facebook. Desactívalo si usas otra app de mensajeria.
87 |
88 | Botón atrás
89 | Al activarlo, el boton atrás será visible en el cajón.
90 |
91 | Botón ir a arriba
92 | Al activarlo, El botón ir a arriba será visible en el cajón.
93 |
94 | Entrar
95 | Compartir
96 |
97 | No has iniciado la sesión
98 | Algo fue mal, intenta iniciar la sesión de nuevo
99 | Algo fue super mal
100 | No se puede acceder a Facebook
101 | Algo funcionó mal en Facebook
102 |
103 | Cargando
104 | %s nuevas notificaciones.
105 | Ver todas las notificaciones
106 |
107 | Permiso denegado
108 |
109 |
--------------------------------------------------------------------------------
/app/src/main/res/values-it/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Toffeed
4 |
5 | Apri navigation drawer
6 | Chiudi navigation drawer
7 |
8 | Utente sconosciuto
9 | Foto Profilo
10 |
11 | Notifiche
12 | News Feed
13 | Richieste di Amicizia
14 | Messaggi
15 | Cerca
16 | Menu Principale
17 |
18 | Entra in Facebook
19 | Torna in Alto
20 | Ricarica
21 | Indietro
22 | Avanti
23 | Impostazioni
24 |
25 | Condividi uno stato
26 | Condividi una foto
27 | Condividi posizione
28 |
29 | Salva immagine
30 | Condividi immagine
31 | Copia link immagine
32 | Condividi link
33 | Copia link
34 | Link copiato negli appunti
35 |
36 | Condividendo immagine…
37 |
38 | Notifiche
39 |
40 | Notifiche in Background
41 | Quando attiva, riceverai notifiche in background da Toffeed.
42 |
43 | Intervallo di notifiche in background
44 | Specifica l\'intervallo di aggiornamento per il servizio di notifiche in background.
45 |
46 | - 5 minuti
47 | - 10 minuti
48 | - 15 minuti
49 | - 30 minuti
50 | - 1 ora
51 | - 2 ore
52 |
53 |
54 | - 300000
55 | - 600000
56 | - 900000
57 | - 1800000
58 | - 3600000
59 | - 7200000
60 |
61 |
62 | Vibrazione
63 | Quando attiva, le notifiche faranno vibrare il tuo device.
64 |
65 | Contenuto
66 |
67 | Accesso alla posizione
68 | Attivala per permettere a Toffeed di ottenere la tua posizione.
69 |
70 | Nascondi l\'editor di stato nel News Feed
71 | Quando attiva, l\'editor di stato sarà nascosto dal News Feed.
72 | Ricarica per applicare le modifiche
73 |
74 | Non visualizzare le immagini
75 | Quando attiva, non verranno visualizzate le immagini. Alcune immagini potrebbero rimanere nella cache.
76 |
77 | Nascondi Contenuti "Sponsorizzati"
78 | Quando attiva, verrà nascosta la pubblicità nel News Feed.
79 |
80 | Navigazione
81 |
82 | Nascondi il Floating Action Button (FAB) quando scrolli
83 | Quando attiva, nasconde il FAB quando scrolli in basso
84 |
85 | Messaggistica
86 | Quando attiva, permetterà di accedere ai Messaggi di Facebook. Disattiva questa opzione se hai un\'app di messaggistica (Facebook Messenger).
87 |
88 | Tasto "Indietro"
89 | Quando attiva, il tasto "Indietro" sarà visibile nel menu laterale.
90 |
91 | Tasto "Torna in Alto"
92 | Quando attiva, il tasto "Torna in Alto" sarà visibile nel menu laterale.
93 |
94 | Entra
95 | Condividi
96 |
97 | Non hai effettuato l\'accesso
98 | Qualcosa è andato storto, prova a eseguire di nuovo l\'accesso
99 | Qualcosa è andato molto storto
100 | Impossibile accedere a Facebook
101 | Qualcosa è andato storto alla fine di Facebook
102 |
103 | Caricamento
104 | %s nuove notifiche.
105 | Mostra tutte le Notifiche
106 |
107 | Permesso negato
108 |
109 |
--------------------------------------------------------------------------------
/app/src/main/res/values-pt-rBR/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Toffeed
4 |
5 | Abrir gaveta de navegação
6 | Fechar gaveta de navegação
7 |
8 | Usuário desconhecido
9 | Foto do perfil
10 |
11 | Notificações
12 | Feed de Notícias
13 | Solicitações de Amizade
14 | Mensagens
15 | Pesquisar
16 | Menu Principal
17 |
18 | Login no Facebook
19 | Ir para o Topo
20 | Recarregar
21 | Voltar
22 | Avançar
23 | Configurações
24 |
25 | Postar um status
26 | Postar uma foto
27 | Check-in
28 |
29 | Salvar imagem
30 | Compartilhar imagem
31 | Copiar link da imagem
32 | Compartilhar link
33 | Copiar link
34 |
35 | Compartilhando imagem…
36 | Link copiado para área de transferência
37 |
38 | Notificações
39 |
40 | Notificações em segundo plano
41 | Quando ativado, você receberá notificações em segundo plano do Facebook.
42 |
43 | Intervalo de Notificações em segundo plano
44 | Especifique o intervalo de atualização para o serviço de notificação.
45 |
46 | - 5 minutos
47 | - 10 minutos
48 | - 15 minutos
49 | - 30 minutos
50 | - 1 hora
51 | - 2 horas
52 |
53 |
54 | - 300000
55 | - 600000
56 | - 900000
57 | - 1800000
58 | - 3600000
59 | - 7200000
60 |
61 |
62 | Vibrar
63 | Quando ativado, as notificações farão com que o seu dispositivo vibre.
64 |
65 | Conteúdo
66 |
67 | Acesso à localização
68 | Ative isso para permitir que o Facebook obtenha a sua localização.
69 |
70 | Esconder o editor de Status no Feed de Notícias
71 | Quando ativado, o editor de Status será escondido no Feed de Notícias.
72 | Atualizar para que as alterações entrem em vigor
73 |
74 | Não carregar imagens
75 | Quando ativado, não vai baixar imagens. Algumas imagens podem permanecer no cache.
76 |
77 | Esconder Conteúdo Patrocinado
78 | Quando ativado, irá esconder Conteúdo "Patrocinado" (anúncios) do Feed de notícias.
79 |
80 | Navegação
81 |
82 | Esconder Botão de Ação Flutuante (FAB) durante a navegação
83 | Esconder o FAB quando estiver navegando para baixo
84 |
85 | Mensagens
86 | Quando ativado, irá proporcionar o acesso as Mensagens do Facebook. Desative se você tiver um aplicativo de mensagens.
87 |
88 | Botão "Voltar"
89 | Quando ativado, o botão de voltar ficará visível.
90 |
91 | Botão "Ir para o Topo"
92 | Quando ativado, o botão "Ir para o Topo" ficará visível.
93 |
94 | Login
95 | Compartilhar
96 |
97 | Você não está logado
98 | Algo deu errado, por favor tente fazer login novamente
99 | Algo deu muito errado
100 | Não foi possível acessar o Facebook
101 | Algo deu errado no fim do Facebook
102 |
103 | Carregando
104 | %s novas notificações.
105 | Ver todas as Notificações
106 |
107 | Permissão negada, ação não pode ser completada
108 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values-zh-rCN/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Toffeed
4 |
5 | 打开侧边栏
6 | 关闭侧边栏
7 |
8 | 未知用户
9 | 个人头像
10 |
11 | 通知
12 | 动态消息
13 | 交友邀请
14 | 讯息
15 | 搜寻
16 | 主选单
17 |
18 | 登入Facebook
19 | 回到顶部
20 | 重新整理
21 | 返回
22 | 前进
23 | 设定
24 |
25 | 近况更新
26 | 新增相片
27 | 打卡
28 |
29 | 下载图片
30 | 分享图片
31 | 复制图片网址
32 | 分享连结
33 | 复制连结网址
34 |
35 | 图片下载中…
36 | 网址已复制到剪贴簿
37 |
38 | 通知
39 |
40 | 后台通知
41 | 开启后,你将可以收到Facebook的后台通知。
42 |
43 | 后台通知时间间隔
44 | 设定「后台通知」的更新频率。
45 |
46 | - 5 分钟
47 | - 10 分钟
48 | - 15 分钟
49 | - 30 分钟
50 | - 1 小时
51 | - 2 小时
52 |
53 |
54 | - 300000
55 | - 600000
56 | - 900000
57 | - 1800000
58 | - 3600000
59 | - 7200000
60 |
61 |
62 | 震动提醒
63 | 开启后,收到后台通知手机会发出震动提醒。
64 |
65 | 内容
66 |
67 | GPS定位
68 | 开启后,将允许Facebook使用GPS定位功能。 (打卡很方便)
69 |
70 | 隐藏Facebook预设的文字编辑区
71 | 这项功能开启后,Facebook预设的文字编辑区将会被隐藏,但是请放心,Toffeed内建的更好用。
72 | 重新整理让刚刚的设定生效
73 |
74 | 不要载入任何图片
75 | 这项功能开启后,系统将不会下载任何图片。 (部分图片有可能会留存在快取中,这是正常现象。)
76 |
77 | 隐藏「赞助」的内容
78 | 这项功能开启后,系统将会隐藏你涂鸦墙上显示「赞助」的内容(也就是俗称的挡广告)。
79 |
80 | 导航
81 |
82 | 滑动时,隐藏悬浮操作按钮
83 | 当你往下滑动阅读时,隐藏右下角的悬浮操作按钮,避免挡到部分内容。
84 |
85 | 讯息功能
86 | 启动这项功能后,「讯息」按钮将会出现在左侧选单当中,方便你直接阅读讯息。如果你想继续使用脸书的「Messenger」软体,可以不要启动这项功能。
87 |
88 | 返回按钮
89 | 启动这项功能后,「返回」按钮将会出现在左侧选单当中。
90 |
91 | 回到顶部按钮
92 | 启动这项功能后,「回到顶部」按钮将会出现在左侧选单当中。
93 |
94 | 登入
95 | 分享
96 |
97 | 您尚未登入
98 | 出了点状况,请您重新登入一次
99 | 哎呀,出了大麻烦
100 | 目前无法连线到Facebook
101 | 脸书的伺服器出了些问题
102 |
103 | 读取中
104 | %s 新通知
105 | 查看全部通知
106 |
107 | 没有权限
108 |
--------------------------------------------------------------------------------
/app/src/main/res/values-zh-rTW/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Toffeed
4 |
5 | 打開側邊選單
6 | 關閉側邊選單
7 |
8 | 未知用戶
9 | 個人頭像
10 |
11 | 通知
12 | 動態消息
13 | 交友邀請
14 | 訊息
15 | 搜尋
16 | 主選單
17 |
18 | 登入Facebook
19 | 回到頂部
20 | 重新整理
21 | 返回
22 | 前進
23 | 設定
24 |
25 | 近況更新
26 | 新增相片
27 | 打卡
28 |
29 | 下載圖片
30 | 分享圖片
31 | 複製圖片網址
32 | 分享連結
33 | 複製連結網址
34 |
35 | 圖片下載中…
36 | 網址已複製到剪貼簿
37 |
38 | 通知
39 |
40 | 後台通知
41 | 開啟後,你將可以收到Facebook的後台通知。
42 |
43 | 後台通知時間間隔
44 | 設定「後台通知」的更新頻率。
45 |
46 | - 5 分鐘
47 | - 10 分鐘
48 | - 15 分鐘
49 | - 30 分鐘
50 | - 1 小時
51 | - 2 小時
52 |
53 |
54 | - 300000
55 | - 600000
56 | - 900000
57 | - 1800000
58 | - 3600000
59 | - 7200000
60 |
61 |
62 | 震動提醒
63 | 開啟後,收到後台通知手機會發出震動提醒。
64 |
65 | 內容
66 |
67 | GPS定位
68 | 開啟後,將允許Facebook使用GPS定位功能。(打卡很方便)
69 |
70 | 隱藏Facebook預設的文字編輯區
71 | 這項功能開啟後,Facebook預設的文字編輯區將會被隱藏,但是請放心,Toffeed內建的更好用。
72 | 重新整理讓剛剛的設定生效
73 |
74 | 不要載入任何圖片
75 | 這項功能開啟後,系統將不會下載任何圖片。(部分圖片有可能會留存在快取中,這是正常現象。)
76 |
77 | 隱藏「贊助」的內容
78 | 這項功能開啟後,系統將會隱藏你塗鴉牆上顯示「贊助」的內容(也就是俗稱的擋廣告)。
79 |
80 | 導航
81 |
82 | 滑動時,隱藏懸浮操作按鈕
83 | 當你往下滑動閱讀時,隱藏右下角的懸浮操作按鈕,避免擋到部分內容。
84 |
85 | 訊息功能
86 | 啟動這項功能後,「訊息」按鈕將會出現在左側選單當中,方便你直接閱讀訊息。如果你想繼續使用臉書的「Messenger」軟體,可以不要啟動這項功能。
87 |
88 | 返回按鈕
89 | 啟動這項功能後,「返回」按鈕將會出現在左側選單當中。
90 |
91 | 回到頂部按鈕
92 | 啟動這項功能後,「回到頂部」按鈕將會出現在左側選單當中。
93 |
94 | 登入
95 | 分享
96 |
97 | 您尚未登入
98 | 出了點狀況,請您重新登入一次
99 | 哎呀,出了大麻煩
100 | 目前無法連線到Facebook
101 | 臉書的伺服器出了些問題
102 |
103 | 讀取中
104 | %s 新通知
105 | 查看全部通知
106 |
107 | 沒有權限
108 |
109 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3b5998
4 | #243c6d
5 | #FF4444
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 160dp
5 |
6 | 16dp
7 | 16dp
8 |
9 | 4dp
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Toffeed
4 |
5 | 422483854616848
6 |
7 | Open navigation drawer
8 | Close navigation drawer
9 |
10 | Unknown user
11 | Profile Picture
12 |
13 | Notifications
14 | News Feed
15 | Top stories
16 | Most Recent
17 | Friend Requests
18 | Messages
19 | Search
20 | Main Menu
21 |
22 | Login to Facebook
23 | Jump to Top
24 | Reload
25 | Back
26 | Forward
27 | Settings
28 |
29 | Post a status
30 | Post a photo
31 | Check In
32 |
33 | Save image
34 | Share image
35 | Copy image link
36 | Share link
37 | Copy link
38 |
39 | Sharing image…
40 | Link copied to clipboard
41 |
42 | Notifications
43 |
44 | Background Notifications
45 | When enabled, you will receive background notifications from Facebook.
46 |
47 | Background Notifications interval
48 | Specify the update interval for the notification service.
49 |
50 | - 5 minutes
51 | - 10 minutes
52 | - 15 minutes
53 | - 30 minutes
54 | - 1 hour
55 | - 2 hours
56 |
57 |
58 | - 300000
59 | - 600000
60 | - 900000
61 | - 1800000
62 | - 3600000
63 | - 7200000
64 |
65 |
66 | Vibrate
67 | When enabled, notifications will make your device vibrate.
68 |
69 | Content
70 |
71 | Location access
72 | Enable this to allow Facebook to obtain your location.
73 |
74 | Most Recent in Navigation Menu
75 | Enable this to show the Most Recent button in the Navigation Menu.
76 |
77 | Hide Status editor in News Feed
78 | When enabled, the status editor will be hidden on the News Feed.
79 | Reload for changes to take effect
80 |
81 | Don\'t load images
82 | When enabled, will not download images. Some images may remain in the cache.
83 |
84 | Hide "Sponsored" Content
85 | When enabled, will hide "Sponsored" Content (ads) from the News Feed.
86 |
87 | Hide Birthdays
88 | When enabled, will hide birthdays from the News Feed.
89 |
90 | Navigation
91 |
92 | Hide Menu bar
93 | When enabled, the Facebook Menu bar will be hidden.
94 |
95 | Hide Floating Action Button on scroll
96 | Hide the FAB when scrolling down
97 |
98 | Messaging
99 | When enabled, will provide access to Facebook Messages. Disable if you have a messaging app.
100 |
101 | Back button
102 | When enabled, the back button will be visible in the draw.
103 |
104 | Jump to Top button
105 | When enabled, the Jump to Top button will be visible in the draw.
106 |
107 | Login
108 | Share
109 |
110 | You are not logged in
111 | Something went wrong, please try logging in again
112 | Something went super wrong
113 | Could not access Facebook
114 | Something went wrong on Facebook\'s end
115 |
116 | Loading
117 | %s new notifications.
118 | View all Notifications
119 |
120 | Permission denied, cannot complete action
121 |
122 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
16 |
21 |
22 |
23 |
28 |
33 |
38 |
43 |
48 |
53 |
54 |
55 |
60 |
65 |
70 |
75 |
80 |
81 |
--------------------------------------------------------------------------------
/app/src/test/java/me/jakelane/wrapperforfacebook/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package me.jakelane.wrapperforfacebook;
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 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.0.0'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | mavenCentral()
18 | jcenter()
19 | maven {
20 | url "https://jitpack.io"
21 | }
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JakeLane/Toffeed/700f63e6f04265ae6d09fcf6c5174d5f86410429/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun May 22 12:09:19 ACST 2016
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.10-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------