├── app
├── .gitignore
├── multidex-config.txt
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── drawable
│ │ │ │ ├── ic_alarm_black_24dp.png
│ │ │ │ ├── ic_check_white_48dp.png
│ │ │ │ ├── ic_history_black_18dp.png
│ │ │ │ ├── ic_history_black_48dp.png
│ │ │ │ ├── ic_history_white_24dp.png
│ │ │ │ ├── ic_perm_identity_black_24dp.png
│ │ │ │ ├── ic_remove_red_eye_black_48dp.png
│ │ │ │ ├── ic_remove_red_eye_white_24dp.png
│ │ │ │ ├── patrick_brinksma_382458_unsplash.jpg
│ │ │ │ └── side_nav_bar.xml
│ │ │ ├── values
│ │ │ │ ├── secret.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── strings.xml
│ │ │ │ ├── arrays.xml
│ │ │ │ └── styles.xml
│ │ │ ├── drawable-hdpi
│ │ │ │ ├── ic_alarm_black_24dp.png
│ │ │ │ ├── ic_check_white_48dp.png
│ │ │ │ ├── ic_history_black_18dp.png
│ │ │ │ ├── ic_history_black_48dp.png
│ │ │ │ ├── ic_history_white_24dp.png
│ │ │ │ ├── ic_perm_identity_black_24dp.png
│ │ │ │ ├── ic_remove_red_eye_black_48dp.png
│ │ │ │ └── ic_remove_red_eye_white_24dp.png
│ │ │ ├── drawable-mdpi
│ │ │ │ ├── ic_alarm_black_24dp.png
│ │ │ │ ├── ic_check_white_48dp.png
│ │ │ │ ├── ic_history_black_18dp.png
│ │ │ │ ├── ic_history_black_48dp.png
│ │ │ │ ├── ic_history_white_24dp.png
│ │ │ │ ├── ic_perm_identity_black_24dp.png
│ │ │ │ ├── ic_remove_red_eye_black_48dp.png
│ │ │ │ └── ic_remove_red_eye_white_24dp.png
│ │ │ ├── drawable-xhdpi
│ │ │ │ ├── ic_alarm_black_24dp.png
│ │ │ │ ├── ic_check_white_48dp.png
│ │ │ │ ├── ic_history_black_18dp.png
│ │ │ │ ├── ic_history_black_48dp.png
│ │ │ │ ├── ic_history_white_24dp.png
│ │ │ │ ├── ic_perm_identity_black_24dp.png
│ │ │ │ ├── ic_remove_red_eye_black_48dp.png
│ │ │ │ └── ic_remove_red_eye_white_24dp.png
│ │ │ ├── drawable-xxhdpi
│ │ │ │ ├── ic_alarm_black_24dp.png
│ │ │ │ ├── ic_check_white_48dp.png
│ │ │ │ ├── ic_history_black_18dp.png
│ │ │ │ ├── ic_history_black_48dp.png
│ │ │ │ ├── ic_history_white_24dp.png
│ │ │ │ ├── ic_perm_identity_black_24dp.png
│ │ │ │ ├── ic_remove_red_eye_black_48dp.png
│ │ │ │ └── ic_remove_red_eye_white_24dp.png
│ │ │ ├── xml
│ │ │ │ ├── default_configs.xml
│ │ │ │ └── analytics.xml
│ │ │ ├── values-v21
│ │ │ │ └── styles.xml
│ │ │ ├── values-w820dp
│ │ │ │ └── dimens.xml
│ │ │ ├── menu
│ │ │ │ ├── main.xml
│ │ │ │ └── activity_main_drawer.xml
│ │ │ ├── layout
│ │ │ │ ├── content_main.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── app_bar_main.xml
│ │ │ │ ├── fragment_scheduler_disabled.xml
│ │ │ │ ├── nav_header_main.xml
│ │ │ │ ├── fragment_privacy_policy.xml
│ │ │ │ ├── fragment_welcome.xml
│ │ │ │ ├── fragment_scheduler_enabled.xml
│ │ │ │ └── fragment_settings.xml
│ │ │ └── raw
│ │ │ │ └── privacy_policy.md
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── hasmobi
│ │ │ │ └── eyerest
│ │ │ │ ├── helpers
│ │ │ │ ├── IShowHideScheduler.java
│ │ │ │ └── RequestDrawOverAppsPermission.java
│ │ │ │ ├── base
│ │ │ │ ├── Prefs.java
│ │ │ │ ├── Constants.java
│ │ │ │ └── Application.java
│ │ │ │ ├── broadcast_receivers
│ │ │ │ └── OnBootBroadcastReceiver.java
│ │ │ │ ├── custom_views
│ │ │ │ ├── SquareImageView.java
│ │ │ │ ├── OverlayView.java
│ │ │ │ └── DiscreeteSeekBar.java
│ │ │ │ ├── billingutil
│ │ │ │ ├── Base64DecoderException.java
│ │ │ │ ├── IabException.java
│ │ │ │ ├── IabResult.java
│ │ │ │ ├── SkuDetails.java
│ │ │ │ ├── Purchase.java
│ │ │ │ ├── Inventory.java
│ │ │ │ └── Security.java
│ │ │ │ ├── fragments
│ │ │ │ ├── main
│ │ │ │ │ ├── SchedulerDisabledFragment.java
│ │ │ │ │ ├── WelcomeFragment.java
│ │ │ │ │ ├── SchedulerEnabledFragment.java
│ │ │ │ │ └── SettingsFragment.java
│ │ │ │ └── PrivacyPolicyFragment.java
│ │ │ │ ├── services
│ │ │ │ ├── MyFirebaseMessagingService.java
│ │ │ │ ├── SchedulerService.java
│ │ │ │ └── OverlayService.java
│ │ │ │ └── activities
│ │ │ │ └── MainActivity.java
│ │ ├── AndroidManifest.xml
│ │ └── aidl
│ │ │ └── com
│ │ │ └── android
│ │ │ └── vending
│ │ │ └── billing
│ │ │ └── IInAppBillingService.aidl
│ └── com
│ │ └── android
│ │ └── vending
│ │ └── billing
│ │ └── IInAppBillingService.aidl
├── proguard-rules.pro
├── google-services.json
├── build.gradle
└── app.iml
├── settings.gradle
├── .idea
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── markdown-navigator
│ └── profiles_settings.xml
├── vcs.xml
├── modules.xml
├── runConfigurations.xml
├── compiler.xml
├── gradle.xml
├── misc.xml
└── markdown-navigator.xml
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── README.md
├── .circleci
└── config.yml
├── gradle.properties
├── .gitignore
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | app.iml
3 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/app/multidex-config.txt:
--------------------------------------------------------------------------------
1 | com/google/firebase/provider/FirebaseInitProvider.class
2 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
25 | * The service is triggered:
26 | * - On device boot
27 | * - Manually, when you toggle the scheduler feature from the app
28 | */
29 | public class SchedulerService extends Service {
30 |
31 | public SchedulerService() {
32 | }
33 |
34 | @Override
35 | public void onCreate() {
36 | Log.d(getClass().toString(), "onCreate");
37 |
38 | super.onCreate();
39 |
40 | final AlarmManager am = (AlarmManager) getBaseContext().getSystemService(Context.ALARM_SERVICE);
41 |
42 | // Schedule this service to be restarted every 30 minutes
43 | Intent iEnd = new Intent(getBaseContext(), SchedulerService.class);
44 | PendingIntent piEnd = PendingIntent.getService(getBaseContext(), 0, iEnd, PendingIntent.FLAG_UPDATE_CURRENT);
45 | am.setRepeating(AlarmManager.RTC, System.currentTimeMillis(), AlarmManager.INTERVAL_HALF_HOUR, piEnd);
46 |
47 | Log.d(getClass().toString(), "Scheduler startup at " + Calendar.getInstance().getTime() + ". Will repeat.");
48 | }
49 |
50 | @Override
51 | public IBinder onBind(Intent intent) {
52 | throw new UnsupportedOperationException("Not yet implemented");
53 | }
54 |
55 | @Override
56 | public int onStartCommand(Intent intent, int flags, int startId) {
57 | Log.d(getClass().toString(), "onStartCommand");
58 |
59 | if (!Prefs.get(getBaseContext()).getBoolean(Constants.PREF_SCHEDULER_ENABLED, false)) {
60 | // If the Scheduler is not enabled, this service should NEVER run again (unless invoked again manually)
61 |
62 | cancelAlarms();
63 | stopSelf();
64 |
65 | Log.d(getClass().toString(), "Scheduler not enabled. Recurring service is stopping itself...");
66 | return START_NOT_STICKY;
67 | } else {
68 | // Scheduler IS enabled
69 |
70 | startOrStopScreenDim();
71 | return START_STICKY;
72 | }
73 | }
74 |
75 | private void startOrStopScreenDim() {
76 | final SharedPreferences sp = Prefs.get(getBaseContext());
77 |
78 | if (sp.getBoolean(Constants.PREF_EYEREST_ENABLED, false)) {
79 | final Calendar cBegin = _getCalendarForStart(getBaseContext());
80 | final Calendar cEnd = _getCalendarForEnd(getBaseContext());
81 |
82 | Log.d(getClass().toString(), "Screen darkens between " + cBegin.getTime() + " and " + cEnd.getTime());
83 | Log.d(getClass().toString(), "Now is " + Calendar.getInstance().getTime());
84 |
85 | Calendar calendar = Calendar.getInstance();
86 |
87 | if (calendar.getTimeInMillis() > cBegin.getTimeInMillis() && calendar.getTimeInMillis() < cEnd.getTimeInMillis()) {
88 | startService(new Intent(getBaseContext(), OverlayService.class));
89 | Log.d(getClass().toString(), "Screen darken started");
90 | Log.d(getClass().toString(), "Remaining seconds until lightening: " + (cEnd.getTimeInMillis() - calendar.getTimeInMillis()) / 1000);
91 | } else {
92 | stopService(new Intent(getBaseContext(), OverlayService.class));
93 | Log.d(getClass().toString(), "Screen darken stopped");
94 | Log.d(getClass().toString(), "Remaining seconds until darkening: " + (cBegin.getTimeInMillis() - calendar.getTimeInMillis()) / 1000);
95 | }
96 | } else {
97 | stopService(new Intent(getBaseContext(), OverlayService.class));
98 | Log.d(getClass().toString(), "Screen darken inactive. Stopping...");
99 | }
100 | }
101 |
102 | @Override
103 | public void onDestroy() {
104 | Log.d(getClass().toString(), "onDestroy");
105 |
106 | cancelAlarms();
107 |
108 | super.onDestroy();
109 | }
110 |
111 | public static Calendar _getCalendarForStart(Context context) {
112 | final SharedPreferences sp = Prefs.get(context);
113 |
114 | final int scheduleFromHour = sp.getInt("scheduleFromHour", 20);
115 | final int scheduleFromMinute = sp.getInt("scheduleFromMinute", 0);
116 |
117 | final Calendar c = Calendar.getInstance();
118 | c.set(Calendar.HOUR_OF_DAY, scheduleFromHour);
119 | c.set(Calendar.MINUTE, scheduleFromMinute);
120 | c.clear(Calendar.SECOND);
121 | return c;
122 | }
123 |
124 | public static Calendar _getCalendarForEnd(Context context) {
125 | final SharedPreferences sp = Prefs.get(context);
126 |
127 | final int hour = sp.getInt("scheduleToHour", 6);
128 | final int minute = sp.getInt("scheduleToMinute", 0);
129 |
130 | final Calendar c = Calendar.getInstance();
131 | c.set(Calendar.HOUR_OF_DAY, hour);
132 | c.set(Calendar.MINUTE, minute);
133 | c.clear(Calendar.SECOND);
134 |
135 | // Roll over +1 day to accommodate cases where the screen dimmer restore
136 | // happens on the next morning
137 | if (c.getTimeInMillis() < _getCalendarForStart(context).getTimeInMillis()) {
138 | c.add(Calendar.DATE, 1);
139 | }
140 | return c;
141 | }
142 |
143 | private void cancelAlarms() {
144 | final AlarmManager am = (AlarmManager) getBaseContext().getSystemService(Context.ALARM_SERVICE);
145 |
146 | Intent iBegin = new Intent(getBaseContext(), SchedulerService.class);
147 | PendingIntent piBegin = PendingIntent.getService(getBaseContext(), 0, iBegin, PendingIntent.FLAG_UPDATE_CURRENT);
148 | am.cancel(piBegin);
149 | }
150 |
151 | static public boolean isEnabled(Context context) {
152 | SharedPreferences prefs = Prefs.get(context);
153 | return prefs.getBoolean(Constants.PREF_SCHEDULER_ENABLED, false);
154 | }
155 |
156 | static public boolean enable(Context context) {
157 | SharedPreferences prefs = Prefs.get(context);
158 |
159 | boolean wasEnabledNow = true;
160 |
161 | if (prefs.getBoolean(Constants.PREF_SCHEDULER_ENABLED, false)) {
162 | wasEnabledNow = false;
163 | } else {
164 | prefs.edit().putBoolean(Constants.PREF_SCHEDULER_ENABLED, true).commit();
165 | }
166 |
167 | Application.refreshServices(context);
168 |
169 | return wasEnabledNow;
170 | }
171 |
172 | static public boolean disable(Context context) {
173 | SharedPreferences prefs = Prefs.get(context);
174 |
175 | boolean wasDisabledNow = true;
176 |
177 | if (!prefs.getBoolean(Constants.PREF_SCHEDULER_ENABLED, false)) {
178 | wasDisabledNow = false;
179 | } else {
180 | prefs.edit().putBoolean(Constants.PREF_SCHEDULER_ENABLED, false).commit();
181 | }
182 |
183 | Application.refreshServices(context);
184 |
185 | return wasDisabledNow;
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hasmobi/eyerest/fragments/main/SchedulerEnabledFragment.java:
--------------------------------------------------------------------------------
1 | package com.hasmobi.eyerest.fragments.main;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.os.Bundle;
6 | import android.os.CountDownTimer;
7 | import androidx.annotation.Nullable;
8 | import androidx.fragment.app.Fragment;
9 |
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.Button;
14 | import android.widget.TextView;
15 |
16 | import com.hasmobi.eyerest.R;
17 | import com.hasmobi.eyerest.base.Application;
18 | import com.hasmobi.eyerest.base.Prefs;
19 | import com.hasmobi.eyerest.helpers.IShowHideScheduler;
20 | import com.hasmobi.eyerest.services.SchedulerService;
21 | import com.wdullaer.materialdatetimepicker.time.RadialPickerLayout;
22 | import com.wdullaer.materialdatetimepicker.time.TimePickerDialog;
23 |
24 | import java.util.Calendar;
25 | import java.util.Locale;
26 | import java.util.TimeZone;
27 |
28 | public class SchedulerEnabledFragment extends Fragment {
29 |
30 | private IShowHideScheduler bridge;
31 |
32 | private CountDownTimer timer;
33 |
34 | @Nullable
35 | @Override
36 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
37 | return inflater.inflate(R.layout.fragment_scheduler_enabled, container, false);
38 | }
39 |
40 | @Override
41 | public void onAttach(Context context) {
42 | super.onAttach(context);
43 |
44 | bridge = ((IShowHideScheduler) getActivity());
45 | }
46 |
47 | @Override
48 | public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) {
49 | super.onViewCreated(view, savedInstanceState);
50 |
51 | final Button bFrom = (Button) view.findViewById(R.id.bFrom);
52 | final Button bTo = (Button) view.findViewById(R.id.bTo);
53 |
54 | Button b = (Button) view.findViewById(R.id.bSchedule);
55 |
56 | b.setOnClickListener(new View.OnClickListener() {
57 | @Override
58 | public void onClick(View v) {
59 | SchedulerService.disable(getContext());
60 | bridge.showOrHideSchedulerUI(false);
61 | }
62 | });
63 |
64 | reloadButtonUIs();
65 |
66 | bFrom.setOnClickListener(new View.OnClickListener() {
67 | @Override
68 | public void onClick(View v) {
69 | final SharedPreferences sp = Prefs.get(getContext());
70 | final int scheduleFromHour = sp.getInt("scheduleFromHour", 20);
71 | final int scheduleFromMinute = sp.getInt("scheduleFromMinute", 0);
72 |
73 | TimePickerDialog dpd = TimePickerDialog.newInstance(
74 | new TimePickerDialog.OnTimeSetListener() {
75 | @Override
76 | public void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute) {
77 | Context ctx = view.getContext();
78 | final SharedPreferences sp = Prefs.get(ctx);
79 | sp.edit().putInt("scheduleFromHour", hourOfDay).putInt("scheduleFromMinute", minute).apply();
80 | reloadButtonUIs();
81 | }
82 | },
83 | scheduleFromHour,
84 | scheduleFromMinute,
85 | true
86 | );
87 | dpd.show(getActivity().getFragmentManager(), "timepicker_dialog");
88 | }
89 | });
90 | bTo.setOnClickListener(new View.OnClickListener() {
91 | @Override
92 | public void onClick(View v) {
93 | final SharedPreferences sp = Prefs.get(getContext());
94 | final int scheduleToHour = sp.getInt("scheduleToHour", 6);
95 | final int scheduleToMinute = sp.getInt("scheduleToMinute", 0);
96 |
97 | TimePickerDialog dpd = TimePickerDialog.newInstance(
98 | new TimePickerDialog.OnTimeSetListener() {
99 | @Override
100 | public void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute) {
101 | Context ctx = view.getContext();
102 | final SharedPreferences sp = Prefs.get(ctx);
103 | sp.edit().putInt("scheduleToHour", hourOfDay).putInt("scheduleToMinute", minute).apply();
104 | reloadButtonUIs();
105 | }
106 | },
107 | scheduleToHour,
108 | scheduleToMinute,
109 | true
110 | );
111 | dpd.show(getActivity().getFragmentManager(), "timepicker_dialog");
112 | }
113 | });
114 | }
115 |
116 | @Override
117 | public void onPause() {
118 | super.onPause();
119 | if (timer != null) {
120 | timer.cancel();
121 | timer = null;
122 | }
123 | }
124 |
125 | private void reloadButtonUIs() {
126 | if (getView() == null) return;
127 |
128 | final SharedPreferences sp = Prefs.get(getContext());
129 |
130 | final int scheduleFromHour = sp.getInt("scheduleFromHour", 20);
131 | final int scheduleFromMinute = sp.getInt("scheduleFromMinute", 0);
132 |
133 | final int scheduleToHour = sp.getInt("scheduleToHour", 6);
134 | final int scheduleToMinute = sp.getInt("scheduleToMinute", 0);
135 |
136 | final Button bFrom = (Button) getView().findViewById(R.id.bFrom);
137 | final Button bTo = (Button) getView().findViewById(R.id.bTo);
138 | final TextView tvRemaining = (TextView) getView().findViewById(R.id.tvTimeRemaining);
139 |
140 | Calendar cNow = Calendar.getInstance();
141 | Calendar cStart = SchedulerService._getCalendarForStart(getContext());
142 | final Calendar cEnd = SchedulerService._getCalendarForEnd(getContext());
143 |
144 | if (timer != null) {
145 | timer.cancel();
146 | }
147 |
148 | final String time_remaining_to_darken_label = getResources().getString(R.string.time_remaining_to_darken_label);
149 | final String time_remaining_to_lighten_label = getResources().getString(R.string.time_remaining_to_lighten_label);
150 |
151 | tvRemaining.setVisibility(View.VISIBLE);
152 |
153 | if (cNow.getTimeInMillis() > cStart.getTimeInMillis() && cNow.getTimeInMillis() < cEnd.getTimeInMillis()) {
154 | // We are now in the darkening period
155 | long remainingMillisToLighten = cEnd.getTimeInMillis() - cNow.getTimeInMillis();
156 | timer = new CountDownTimer(remainingMillisToLighten, 1000) {
157 |
158 | public void onTick(long millisUntilFinished) {
159 | String t = time_remaining_to_lighten_label + ": " + String.format(Locale.getDefault(), "%tT", (millisUntilFinished - TimeZone.getDefault().getRawOffset()));
160 | tvRemaining.setText(t);
161 | }
162 |
163 | public void onFinish() {
164 | reloadButtonUIs();
165 | }
166 | }.start();
167 | } else if (cNow.getTimeInMillis() < cStart.getTimeInMillis()) {
168 | // Darkening not yet started for today
169 | long remainingMillisToDarken = cStart.getTimeInMillis() - cNow.getTimeInMillis();
170 | timer = new CountDownTimer(remainingMillisToDarken, 1000) {
171 |
172 | public void onTick(long millisUntilFinished) {
173 | String t = time_remaining_to_darken_label + ": " + String.format(Locale.getDefault(), "%tT", (millisUntilFinished - TimeZone.getDefault().getRawOffset()));
174 | tvRemaining.setText(t);
175 | }
176 |
177 | public void onFinish() {
178 | reloadButtonUIs();
179 | }
180 | }.start();
181 | } else {
182 | // Darkening has ended for today
183 | tvRemaining.setVisibility(View.GONE);
184 | }
185 |
186 | bFrom.setText(String.format(Locale.getDefault(), "%02d:%02d", scheduleFromHour, scheduleFromMinute));
187 | bTo.setText(String.format(Locale.getDefault(), "%02d:%02d", scheduleToHour, scheduleToMinute));
188 |
189 | if (getContext() != null) {
190 | Application.refreshServices(getContext());
191 | }
192 | }
193 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/hasmobi/eyerest/services/OverlayService.java:
--------------------------------------------------------------------------------
1 | package com.hasmobi.eyerest.services;
2 |
3 | import android.app.NotificationChannel;
4 | import android.app.NotificationManager;
5 | import android.app.PendingIntent;
6 | import android.app.Service;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.content.SharedPreferences;
10 | import android.graphics.Color;
11 | import android.graphics.PixelFormat;
12 | import android.os.Build;
13 | import android.os.Bundle;
14 | import android.os.IBinder;
15 | import androidx.core.app.NotificationCompat;
16 | import androidx.core.app.TaskStackBuilder;
17 | import android.util.Log;
18 | import android.view.Gravity;
19 | import android.view.WindowManager;
20 |
21 | import com.google.firebase.analytics.FirebaseAnalytics;
22 | import com.hasmobi.eyerest.base.Application;
23 | import com.hasmobi.eyerest.base.Prefs;
24 | import com.hasmobi.eyerest.R;
25 | import com.hasmobi.eyerest.activities.MainActivity;
26 | import com.hasmobi.eyerest.base.Constants;
27 | import com.hasmobi.eyerest.custom_views.OverlayView;
28 |
29 | public class OverlayService extends Service {
30 | private OverlayView mView;
31 |
32 | private static final String NOTIFICATION_CHANNEL_ID = "overlay_service";
33 |
34 | @Override
35 | public IBinder onBind(Intent intent) {
36 | return null;
37 | }
38 |
39 | @Override
40 | public int onStartCommand(Intent intent, int flags, int startId) {
41 | Log.d(getClass().getSimpleName(), "onStartCommand");
42 |
43 | final SharedPreferences sp = Prefs.get(getBaseContext());
44 |
45 | if (!OverlayService.isEnabled(getBaseContext())) {
46 | Log.e(getClass().toString(), "OverlayService enabled but the screen dim is disabled. Shutting down service...");
47 | stopSelf();
48 | return START_NOT_STICKY;
49 | }
50 | if (!Application.canDrawOverlay(getBaseContext())) {
51 | Log.e(getClass().toString(), "Permission not granted, stopping service");
52 | stopSelf();
53 | return START_NOT_STICKY;
54 | }
55 |
56 | int opacityPercent = sp.getInt(Constants.PREF_DIM_LEVEL, 20);
57 | int color = sp.getInt(Constants.PREF_OVERLAY_COLOR, Color.BLACK);
58 |
59 | if (mView == null) {
60 | mView = new OverlayView(this);
61 |
62 | mView.setOpacityPercent(opacityPercent);
63 | mView.setColor(color);
64 |
65 | WindowManager.LayoutParams params = new WindowManager.LayoutParams(
66 | WindowManager.LayoutParams.MATCH_PARENT,
67 | WindowManager.LayoutParams.MATCH_PARENT,
68 | (Build.VERSION.SDK_INT < Build.VERSION_CODES.O ?
69 | WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY :
70 | WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY),
71 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
72 | PixelFormat.TRANSLUCENT);
73 | params.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
74 | params.horizontalMargin = 0;
75 | params.verticalMargin = 0;
76 |
77 | WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
78 |
79 | // Add the viw as screen overlay
80 | wm.addView(mView, params);
81 | } else {
82 | mView.setOpacityPercent(opacityPercent);
83 | mView.setColor(color);
84 |
85 | mView.redraw();
86 | }
87 |
88 | showNotification();
89 |
90 | return START_STICKY;
91 | }
92 |
93 | private void createNotificationChannel() {
94 | // Create the NotificationChannel, but only on API 26+ because
95 | // the NotificationChannel class is new and not in the support library
96 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
97 | CharSequence name = "Allow putting screen overlay";
98 | String description = "";
99 | int importance = NotificationManager.IMPORTANCE_DEFAULT;
100 | NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, name, importance);
101 | channel.setDescription(description);
102 | // Register the channel with the system; you can't change the importance
103 | // or other notification behaviors after this
104 | NotificationManager notificationManager = getSystemService(NotificationManager.class);
105 | notificationManager.createNotificationChannel(channel);
106 | }
107 | }
108 |
109 | private void showNotification() {
110 | this.createNotificationChannel();
111 |
112 | NotificationCompat.Builder mBuilder =
113 | new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
114 | .setSmallIcon(R.drawable.ic_remove_red_eye_white_24dp)
115 | .setContentTitle("Blue light filter active")
116 | .setContentText("Tap to edit settings or disable");
117 |
118 | mBuilder.setOngoing(true);
119 |
120 | // Creates an explicit intent for an Activity in your app
121 | Intent resultIntent = new Intent(this, MainActivity.class);
122 |
123 | // The stack builder object will contain an artificial back stack for the
124 | // started Activity.
125 | // This ensures that navigating backward from the Activity leads out of
126 | // your application to the Home screen.
127 | TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
128 | // Adds the back stack for the Intent (but not the Intent itself)
129 | stackBuilder.addParentStack(MainActivity.class);
130 | // Adds the Intent that starts the Activity to the top of the stack
131 | stackBuilder.addNextIntent(resultIntent);
132 | PendingIntent resultPendingIntent =
133 | stackBuilder.getPendingIntent(
134 | 0,
135 | PendingIntent.FLAG_UPDATE_CURRENT
136 | );
137 | mBuilder.setContentIntent(resultPendingIntent);
138 |
139 | startForeground(1000, mBuilder.build());
140 | }
141 |
142 | @Override
143 | public void onDestroy() {
144 | super.onDestroy();
145 |
146 | Log.d(getClass().getSimpleName(), "onDestroy");
147 |
148 | if (mView != null) {
149 | ((WindowManager) getSystemService(WINDOW_SERVICE)).removeView(mView);
150 | mView = null;
151 | }
152 |
153 | stopForeground(true);
154 | }
155 |
156 | static public boolean isEnabled(Context context) {
157 | SharedPreferences prefs = Prefs.get(context);
158 | return prefs.getBoolean(Constants.PREF_EYEREST_ENABLED, false);
159 | }
160 |
161 | static public boolean enable(Context context) {
162 | SharedPreferences prefs = Prefs.get(context);
163 |
164 | boolean wasEnabledNow = true;
165 |
166 | if (prefs.getBoolean(Constants.PREF_EYEREST_ENABLED, false)) {
167 | wasEnabledNow = false;
168 | } else {
169 | prefs.edit().putBoolean(Constants.PREF_EYEREST_ENABLED, true).apply();
170 | }
171 |
172 | Application.refreshServices(context);
173 |
174 | if (wasEnabledNow) {
175 | FirebaseAnalytics analytics = FirebaseAnalytics.getInstance(context);
176 |
177 | Bundle b = new Bundle();
178 | b.putString(FirebaseAnalytics.Param.ITEM_ID, Constants.ANALYTICS_EVENT_OVERLAY_SERVICE);
179 | b.putString(FirebaseAnalytics.Param.CONTENT_TYPE, "enabled");
180 | analytics.logEvent(FirebaseAnalytics.Event.SELECT_CONTENT, b);
181 | }
182 |
183 | return wasEnabledNow;
184 | }
185 |
186 | static public boolean disable(Context context) {
187 | SharedPreferences prefs = Prefs.get(context);
188 |
189 | boolean wasDisabledNow = true;
190 |
191 | if (!prefs.getBoolean(Constants.PREF_EYEREST_ENABLED, false)) {
192 | wasDisabledNow = false;
193 | } else {
194 | prefs.edit().putBoolean(Constants.PREF_EYEREST_ENABLED, false).apply();
195 | }
196 |
197 | Application.refreshServices(context);
198 |
199 | if (wasDisabledNow) {
200 | FirebaseAnalytics analytics = FirebaseAnalytics.getInstance(context);
201 |
202 | Bundle b = new Bundle();
203 | b.putString(FirebaseAnalytics.Param.ITEM_ID, Constants.ANALYTICS_EVENT_OVERLAY_SERVICE);
204 | b.putString(FirebaseAnalytics.Param.CONTENT_TYPE, "disabled");
205 | analytics.logEvent(FirebaseAnalytics.Event.SELECT_CONTENT, b);
206 | }
207 |
208 | return wasDisabledNow;
209 | }
210 | }
--------------------------------------------------------------------------------
/app/src/com/android/vending/billing/IInAppBillingService.aidl:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.android.vending.billing;
18 |
19 | import android.os.Bundle;
20 |
21 | /**
22 | * InAppBillingService is the service that provides in-app billing version 3 and beyond.
23 | * This service provides the following features:
24 | * 1. Provides a new API to get details of in-app items published for the app including
25 | * price, type, title and description.
26 | * 2. The purchase flow is synchronous and purchase information is available immediately
27 | * after it completes.
28 | * 3. Purchase information of in-app purchases is maintained within the Google Play system
29 | * till the purchase is consumed.
30 | * 4. An API to consume a purchase of an inapp item. All purchases of one-time
31 | * in-app items are consumable and thereafter can be purchased again.
32 | * 5. An API to get current purchases of the user immediately. This will not contain any
33 | * consumed purchases.
34 | *
35 | * All calls will give a response code with the following possible values
36 | * RESULT_OK = 0 - success
37 | * RESULT_USER_CANCELED = 1 - user pressed back or canceled a dialog
38 | * RESULT_BILLING_UNAVAILABLE = 3 - this billing API version is not supported for the type requested
39 | * RESULT_ITEM_UNAVAILABLE = 4 - requested SKU is not available for purchase
40 | * RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API
41 | * RESULT_ERROR = 6 - Fatal error during the API action
42 | * RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned
43 | * RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned
44 | */
45 | interface IInAppBillingService {
46 | /**
47 | * Checks support for the requested billing API version, package and in-app type.
48 | * Minimum API version supported by this interface is 3.
49 | * @param apiVersion the billing version which the app is using
50 | * @param packageName the package name of the calling app
51 | * @param type type of the in-app item being purchased "inapp" for one-time purchases
52 | * and "subs" for subscription.
53 | * @return RESULT_OK(0) on success, corresponding result code on failures
54 | */
55 | int isBillingSupported(int apiVersion, String packageName, String type);
56 |
57 | /**
58 | * Provides details of a list of SKUs
59 | * Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
60 | * with a list JSON strings containing the productId, price, title and description.
61 | * This API can be called with a maximum of 20 SKUs.
62 | * @param apiVersion billing API version that the Third-party is using
63 | * @param packageName the package name of the calling app
64 | * @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
65 | * @return Bundle containing the following key-value pairs
66 | * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
67 | * failure as listed above.
68 | * "DETAILS_LIST" with a StringArrayList containing purchase information
69 | * in JSON format similar to:
70 | * '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00",
71 | * "title : "Example Title", "description" : "This is an example description" }'
72 | */
73 | Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle);
74 |
75 | /**
76 | * Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
77 | * the type, a unique purchase token and an optional developer payload.
78 | * @param apiVersion billing API version that the app is using
79 | * @param packageName package name of the calling app
80 | * @param sku the SKU of the in-app item as published in the developer console
81 | * @param type the type of the in-app item ("inapp" for one-time purchases
82 | * and "subs" for subscription).
83 | * @param developerPayload optional argument to be sent back with the purchase information
84 | * @return Bundle containing the following key-value pairs
85 | * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
86 | * failure as listed above.
87 | * "BUY_INTENT" - PendingIntent to start the purchase flow
88 | *
89 | * The Pending intent should be launched with startIntentSenderForResult. When purchase flow
90 | * has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
91 | * If the purchase is successful, the result data will contain the following key-value pairs
92 | * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
93 | * failure as listed above.
94 | * "INAPP_PURCHASE_DATA" - String in JSON format similar to
95 | * '{"orderId":"12999763169054705758.1371079406387615",
96 | * "packageName":"com.example.app",
97 | * "productId":"exampleSku",
98 | * "purchaseTime":1345678900000,
99 | * "purchaseToken" : "122333444455555",
100 | * "developerPayload":"example developer payload" }'
101 | * "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
102 | * was signed with the private key of the developer
103 | * TODO: change this to app-specific keys.
104 | */
105 | Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type,
106 | String developerPayload);
107 |
108 | /**
109 | * Returns the current SKUs owned by the user of the type and package name specified along with
110 | * purchase information and a signature of the data to be validated.
111 | * This will return all SKUs that have been purchased in V3 and managed items purchased using
112 | * V1 and V2 that have not been consumed.
113 | * @param apiVersion billing API version that the app is using
114 | * @param packageName package name of the calling app
115 | * @param type the type of the in-app items being requested
116 | * ("inapp" for one-time purchases and "subs" for subscription).
117 | * @param continuationToken to be set as null for the first call, if the number of owned
118 | * skus are too many, a continuationToken is returned in the response bundle.
119 | * This method can be called again with the continuation token to get the next set of
120 | * owned skus.
121 | * @return Bundle containing the following key-value pairs
122 | * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
123 | * failure as listed above.
124 | * "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
125 | * "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
126 | * "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
127 | * of the purchase information
128 | * "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
129 | * next set of in-app purchases. Only set if the
130 | * user has more owned skus than the current list.
131 | */
132 | Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken);
133 |
134 | /**
135 | * Consume the last purchase of the given SKU. This will result in this item being removed
136 | * from all subsequent responses to getPurchases() and allow re-purchase of this item.
137 | * @param apiVersion billing API version that the app is using
138 | * @param packageName package name of the calling app
139 | * @param purchaseToken token in the purchase information JSON that identifies the purchase
140 | * to be consumed
141 | * @return 0 if consumption succeeded. Appropriate error values for failures.
142 | */
143 | int consumePurchase(int apiVersion, String packageName, String purchaseToken);
144 | }
145 |
--------------------------------------------------------------------------------
/app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.android.vending.billing;
18 |
19 | import android.os.Bundle;
20 |
21 | /**
22 | * InAppBillingService is the service that provides in-app billing version 3 and beyond.
23 | * This service provides the following features:
24 | * 1. Provides a new API to get details of in-app items published for the app including
25 | * price, type, title and description.
26 | * 2. The purchase flow is synchronous and purchase information is available immediately
27 | * after it completes.
28 | * 3. Purchase information of in-app purchases is maintained within the Google Play system
29 | * till the purchase is consumed.
30 | * 4. An API to consume a purchase of an inapp item. All purchases of one-time
31 | * in-app items are consumable and thereafter can be purchased again.
32 | * 5. An API to get current purchases of the user immediately. This will not contain any
33 | * consumed purchases.
34 | *
35 | * All calls will give a response code with the following possible values
36 | * RESULT_OK = 0 - success
37 | * RESULT_USER_CANCELED = 1 - user pressed back or canceled a dialog
38 | * RESULT_BILLING_UNAVAILABLE = 3 - this billing API version is not supported for the type requested
39 | * RESULT_ITEM_UNAVAILABLE = 4 - requested SKU is not available for purchase
40 | * RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API
41 | * RESULT_ERROR = 6 - Fatal error during the API action
42 | * RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned
43 | * RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned
44 | */
45 | interface IInAppBillingService {
46 | /**
47 | * Checks support for the requested billing API version, package and in-app type.
48 | * Minimum API version supported by this interface is 3.
49 | * @param apiVersion the billing version which the app is using
50 | * @param packageName the package name of the calling app
51 | * @param type type of the in-app item being purchased "inapp" for one-time purchases
52 | * and "subs" for subscription.
53 | * @return RESULT_OK(0) on success, corresponding result code on failures
54 | */
55 | int isBillingSupported(int apiVersion, String packageName, String type);
56 |
57 | /**
58 | * Provides details of a list of SKUs
59 | * Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
60 | * with a list JSON strings containing the productId, price, title and description.
61 | * This API can be called with a maximum of 20 SKUs.
62 | * @param apiVersion billing API version that the Third-party is using
63 | * @param packageName the package name of the calling app
64 | * @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
65 | * @return Bundle containing the following key-value pairs
66 | * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
67 | * failure as listed above.
68 | * "DETAILS_LIST" with a StringArrayList containing purchase information
69 | * in JSON format similar to:
70 | * '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00",
71 | * "title : "Example Title", "description" : "This is an example description" }'
72 | */
73 | Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle);
74 |
75 | /**
76 | * Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
77 | * the type, a unique purchase token and an optional developer payload.
78 | * @param apiVersion billing API version that the app is using
79 | * @param packageName package name of the calling app
80 | * @param sku the SKU of the in-app item as published in the developer console
81 | * @param type the type of the in-app item ("inapp" for one-time purchases
82 | * and "subs" for subscription).
83 | * @param developerPayload optional argument to be sent back with the purchase information
84 | * @return Bundle containing the following key-value pairs
85 | * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
86 | * failure as listed above.
87 | * "BUY_INTENT" - PendingIntent to start the purchase flow
88 | *
89 | * The Pending intent should be launched with startIntentSenderForResult. When purchase flow
90 | * has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
91 | * If the purchase is successful, the result data will contain the following key-value pairs
92 | * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
93 | * failure as listed above.
94 | * "INAPP_PURCHASE_DATA" - String in JSON format similar to
95 | * '{"orderId":"12999763169054705758.1371079406387615",
96 | * "packageName":"com.example.app",
97 | * "productId":"exampleSku",
98 | * "purchaseTime":1345678900000,
99 | * "purchaseToken" : "122333444455555",
100 | * "developerPayload":"example developer payload" }'
101 | * "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
102 | * was signed with the private key of the developer
103 | * TODO: change this to app-specific keys.
104 | */
105 | Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type,
106 | String developerPayload);
107 |
108 | /**
109 | * Returns the current SKUs owned by the user of the type and package name specified along with
110 | * purchase information and a signature of the data to be validated.
111 | * This will return all SKUs that have been purchased in V3 and managed items purchased using
112 | * V1 and V2 that have not been consumed.
113 | * @param apiVersion billing API version that the app is using
114 | * @param packageName package name of the calling app
115 | * @param type the type of the in-app items being requested
116 | * ("inapp" for one-time purchases and "subs" for subscription).
117 | * @param continuationToken to be set as null for the first call, if the number of owned
118 | * skus are too many, a continuationToken is returned in the response bundle.
119 | * This method can be called again with the continuation token to get the next set of
120 | * owned skus.
121 | * @return Bundle containing the following key-value pairs
122 | * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
123 | * failure as listed above.
124 | * "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
125 | * "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
126 | * "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
127 | * of the purchase information
128 | * "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
129 | * next set of in-app purchases. Only set if the
130 | * user has more owned skus than the current list.
131 | */
132 | Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken);
133 |
134 | /**
135 | * Consume the last purchase of the given SKU. This will result in this item being removed
136 | * from all subsequent responses to getPurchases() and allow re-purchase of this item.
137 | * @param apiVersion billing API version that the app is using
138 | * @param packageName package name of the calling app
139 | * @param purchaseToken token in the purchase information JSON that identifies the purchase
140 | * to be consumed
141 | * @return 0 if consumption succeeded. Appropriate error values for failures.
142 | */
143 | int consumePurchase(int apiVersion, String packageName, String purchaseToken);
144 | }
145 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hasmobi/eyerest/fragments/main/SettingsFragment.java:
--------------------------------------------------------------------------------
1 | package com.hasmobi.eyerest.fragments.main;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.content.res.TypedArray;
6 | import android.graphics.Bitmap;
7 | import android.graphics.Color;
8 | import android.graphics.drawable.BitmapDrawable;
9 | import android.os.Bundle;
10 | import android.util.Log;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.widget.AdapterView;
15 | import android.widget.BaseAdapter;
16 | import android.widget.Button;
17 | import android.widget.GridView;
18 | import android.widget.ImageView;
19 | import android.widget.LinearLayout;
20 |
21 | import androidx.annotation.ColorInt;
22 | import androidx.annotation.Nullable;
23 | import androidx.fragment.app.DialogFragment;
24 | import androidx.fragment.app.Fragment;
25 |
26 | import com.hasmobi.eyerest.R;
27 | import com.hasmobi.eyerest.activities.MainActivity;
28 | import com.hasmobi.eyerest.base.Application;
29 | import com.hasmobi.eyerest.base.Constants;
30 | import com.hasmobi.eyerest.base.Prefs;
31 | import com.hasmobi.eyerest.custom_views.DiscreeteSeekBar;
32 | import com.hasmobi.eyerest.custom_views.SquareImageView;
33 | import com.hasmobi.eyerest.fragments.PrivacyPolicyFragment;
34 | import com.hasmobi.eyerest.services.SchedulerService;
35 | import com.thebluealliance.spectrum.SpectrumDialog;
36 |
37 | import java.util.ArrayList;
38 | import java.util.List;
39 |
40 | public class SettingsFragment extends Fragment {
41 |
42 | @Nullable
43 | @Override
44 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
45 | return inflater.inflate(R.layout.fragment_settings, container, false);
46 | }
47 |
48 | @Override
49 | public void onViewCreated(View root, @Nullable Bundle savedInstanceState) {
50 | super.onViewCreated(root, savedInstanceState);
51 |
52 | /*final ImageView ivEnableDisable = (ImageView) root.findViewById(R.id.ivEnableDisable);
53 |
54 | if (OverlayService.isEnabled(getContext())) {
55 | ivEnableDisable.setAlpha(1.0f);
56 | } else {
57 | ivEnableDisable.setAlpha(0.5f);
58 | }
59 |
60 | ivEnableDisable.setOnClickListener(new View.OnClickListener() {
61 | @Override
62 | public void onClick(View v) {
63 | // Enable or disable the dimmer service
64 | if (OverlayService.isEnabled(getContext())) {
65 | OverlayService.disable(getContext());
66 | v.setAlpha(0.5f);
67 | } else {
68 | OverlayService.enable(getContext());
69 | v.setAlpha(1);
70 | }
71 | }
72 | });*/
73 |
74 | root.findViewById(R.id.bPrivacyPolicy).setOnClickListener(new View.OnClickListener() {
75 | @Override
76 | public void onClick(View view) {
77 | PrivacyPolicyFragment fPrivacyPolicy = PrivacyPolicyFragment.newInstance();
78 | fPrivacyPolicy.show(getActivity().getSupportFragmentManager(), this.getClass().toString());
79 | }
80 | });
81 |
82 | final Button bColorPicker = root.findViewById(R.id.bColorPicker);
83 | bColorPicker.setOnClickListener(new View.OnClickListener() {
84 | @Override
85 | public void onClick(View view) {
86 | TypedArray ta = getResources().obtainTypedArray(R.array.mdcolor_500);
87 | int[] colors = new int[ta.length()];
88 | for (int i = 0; i < ta.length(); i++) {
89 | String hex = ta.getString(i);
90 | colors[i] = Color.parseColor(hex);
91 | }
92 | ta.recycle();
93 |
94 | SharedPreferences prefs = Prefs.get(view.getContext());
95 | int preselectColor = prefs.getInt(Constants.PREF_OVERLAY_COLOR, colors[0]);
96 |
97 | DialogFragment colorPickerDialog = new SpectrumDialog.Builder(getContext())
98 | .setTitle("Select a color")
99 | .setColors(colors)
100 | .setSelectedColor(preselectColor)
101 | .setOnColorSelectedListener(new SpectrumDialog.OnColorSelectedListener() {
102 | @Override
103 | public void onColorSelected(boolean positiveResult, @ColorInt int color) {
104 | if (!positiveResult) return;
105 |
106 | SharedPreferences sp = Prefs.get(getContext());
107 |
108 | sp.edit().putInt(Constants.PREF_OVERLAY_COLOR, color).apply();
109 |
110 | Application.refreshServices(getContext());
111 |
112 | refreshUI();
113 | }
114 | })
115 | .build();
116 | colorPickerDialog.show(getFragmentManager(), "color_picker");
117 | }
118 | });
119 |
120 | refreshUI();
121 |
122 | final GridView gridview = root.findViewById(R.id.gridColors);
123 | final DiscreeteSeekBar sb = root.findViewById(R.id.sbDarkenIntensity);
124 |
125 | final ColorsAdapter adapter = new ColorsAdapter(getContext());
126 | gridview.setAdapter(adapter);
127 |
128 | SharedPreferences sp = Prefs.get(getContext());
129 |
130 | int opacityPercent = sp.getInt(Constants.PREF_DIM_LEVEL, 20);
131 | final int currentColor = sp.getInt("overlay_color", Color.BLACK);
132 |
133 | Log.d(getClass().toString(), "Restoring darkening intensity to " + opacityPercent + " from preferences");
134 | sb.setProgress(opacityPercent);
135 |
136 | int totalColors = adapter.getCount();
137 | for (int i = 0; totalColors > i; i += 1) {
138 | OverlayColor c = adapter.getItem(i);
139 | if (c.color == currentColor) {
140 | Log.d(getClass().toString(), "Restored color from preferences: " + c.hex);
141 | adapter.setSelectedPosition(i);
142 | break;
143 | }
144 | }
145 |
146 | // When a different color is selected, preserve it in SharedPreferences
147 | gridview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
148 | public void onItemClick(AdapterView> parent, View v, int position, long id) {
149 | adapter.setSelectedPosition(position);
150 | OverlayColor selectedItem = adapter.getItem(position);
151 |
152 | SharedPreferences sp = Prefs.get(v.getContext());
153 |
154 | sp.edit().putInt("overlay_color", selectedItem.color).apply();
155 |
156 | Application.refreshServices(v.getContext());
157 |
158 | Log.d(getClass().toString(), "Setting new color to " + selectedItem.hex);
159 | }
160 | });
161 |
162 | if (SchedulerService.isEnabled(getContext())) {
163 | ((MainActivity) getActivity()).showOrHideSchedulerUI(true);
164 | } else {
165 | ((MainActivity) getActivity()).showOrHideSchedulerUI(false);
166 | }
167 | }
168 |
169 | private void refreshUI() {
170 | SharedPreferences sp = Prefs.get(getContext());
171 |
172 | final int currentColor = sp.getInt("overlay_color", Color.BLACK);
173 |
174 | final Button bColorPicker = getView().findViewById(R.id.bColorPicker);
175 | bColorPicker.setBackgroundColor(currentColor);
176 | }
177 |
178 | static private class OverlayColor {
179 | public String label;
180 | public String hex;
181 | public int color;
182 |
183 | public OverlayColor(String label, String hex) {
184 | this.label = label;
185 | this.hex = hex;
186 | this.color = Color.parseColor(hex);
187 | }
188 | }
189 |
190 | static private class ColorsAdapter extends BaseAdapter {
191 |
192 | private Context mContext;
193 |
194 | private int selectedPosition = 0;
195 |
196 | private List