├── app ├── .gitignore ├── src │ └── main │ │ ├── preset1-web.png │ │ ├── ic_launcher-web.png │ │ ├── res │ │ ├── drawable │ │ │ ├── single_preset1.png │ │ │ ├── single_preset2.png │ │ │ ├── single_preset3.png │ │ │ ├── multiple_preset1.png │ │ │ ├── multiple_preset2.png │ │ │ ├── multiple_preset3.png │ │ │ ├── multiple_preset4.png │ │ │ ├── multiple_preset5.png │ │ │ ├── multiple_preset6.png │ │ │ ├── warning_icon.xml │ │ │ ├── action_preview_on_icon.xml │ │ │ ├── action_refresh_icon.xml │ │ │ ├── action_preview_off_icon.xml │ │ │ └── eventlock_icon.xml │ │ ├── values │ │ │ ├── attrs.xml │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── urls.xml │ │ │ ├── styles.xml │ │ │ ├── internal.xml │ │ │ ├── constants.xml │ │ │ └── strings.xml │ │ ├── layout │ │ │ ├── content_main.xml │ │ │ ├── fragment_main.xml │ │ │ └── activity_main.xml │ │ ├── menu │ │ │ └── actions.xml │ │ └── xml │ │ │ └── preferences.xml │ │ ├── assets │ │ └── xposed_init │ │ ├── java │ │ └── com │ │ │ └── gobbledygook │ │ │ └── theawless │ │ │ └── eventlock │ │ │ ├── receivers │ │ │ ├── alarms │ │ │ │ ├── CalendarLoaderAlarm.java │ │ │ │ ├── CurrentEventUpdaterAlarm.java │ │ │ │ └── Alarm.java │ │ │ ├── CommonReceiver.java │ │ │ └── UpdateReceiver.java │ │ │ ├── helper │ │ │ ├── Enums.java │ │ │ ├── XposedUtils.java │ │ │ ├── Utils.java │ │ │ └── Constants.java │ │ │ ├── app │ │ │ ├── VersionPreference.java │ │ │ ├── PresetPreference.java │ │ │ ├── CalendarPreference.java │ │ │ ├── PresetMaker.java │ │ │ ├── MainActivity.java │ │ │ └── SettingsFragment.java │ │ │ ├── hooks │ │ │ ├── XposedUtilsHook.java │ │ │ └── LockscreenHook.java │ │ │ ├── gismo │ │ │ ├── DivisionLinearSnapHelper.java │ │ │ ├── EventsAdapter.java │ │ │ ├── EventViewBuildDirector.java │ │ │ ├── EventsGismo.java │ │ │ ├── LayoutManagerHandler.java │ │ │ └── EventViewBuilder.java │ │ │ ├── services │ │ │ ├── CalendarLoaderService.java │ │ │ └── CurrentEventUpdaterService.java │ │ │ └── events │ │ │ ├── EventsBuildDirector.java │ │ │ └── EventsBuilder.java │ │ └── AndroidManifest.xml └── build.gradle ├── settings.gradle ├── images ├── banner.png ├── presets.jpg ├── lockscreen.jpg ├── mainscreen.jpg ├── multiple_preset1.jpg ├── multiple_preset2.jpg ├── multiple_preset3.jpg └── multiple_preset5.jpg ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .idea ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── vcs.xml ├── modules.xml ├── gradle.xml └── misc.xml ├── gradle.properties ├── .gitignore ├── LICENSE ├── README.md ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/images/banner.png -------------------------------------------------------------------------------- /images/presets.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/images/presets.jpg -------------------------------------------------------------------------------- /images/lockscreen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/images/lockscreen.jpg -------------------------------------------------------------------------------- /images/mainscreen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/images/mainscreen.jpg -------------------------------------------------------------------------------- /app/src/main/preset1-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/app/src/main/preset1-web.png -------------------------------------------------------------------------------- /images/multiple_preset1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/images/multiple_preset1.jpg -------------------------------------------------------------------------------- /images/multiple_preset2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/images/multiple_preset2.jpg -------------------------------------------------------------------------------- /images/multiple_preset3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/images/multiple_preset3.jpg -------------------------------------------------------------------------------- /images/multiple_preset5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/images/multiple_preset5.jpg -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable/single_preset1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/app/src/main/res/drawable/single_preset1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/single_preset2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/app/src/main/res/drawable/single_preset2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/single_preset3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/app/src/main/res/drawable/single_preset3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/multiple_preset1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/app/src/main/res/drawable/multiple_preset1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/multiple_preset2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/app/src/main/res/drawable/multiple_preset2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/multiple_preset3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/app/src/main/res/drawable/multiple_preset3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/multiple_preset4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/app/src/main/res/drawable/multiple_preset4.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/multiple_preset5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/app/src/main/res/drawable/multiple_preset5.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/multiple_preset6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theawless/EventLock/HEAD/app/src/main/res/drawable/multiple_preset6.png -------------------------------------------------------------------------------- /app/src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | com.gobbledygook.theawless.eventlock.hooks.XposedUtilsHook 2 | com.gobbledygook.theawless.eventlock.hooks.LockscreenHook 3 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon May 28 09:43:53 IST 2018 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-4.4-all.zip 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/warning_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/receivers/alarms/CalendarLoaderAlarm.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.receivers.alarms; 2 | 3 | import com.gobbledygook.theawless.eventlock.services.CalendarLoaderService; 4 | 5 | public class CalendarLoaderAlarm extends Alarm { 6 | @Override 7 | protected Class getServiceClass() { 8 | return CalendarLoaderService.class; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/receivers/alarms/CurrentEventUpdaterAlarm.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.receivers.alarms; 2 | 3 | import com.gobbledygook.theawless.eventlock.services.CurrentEventUpdaterService; 4 | 5 | public class CurrentEventUpdaterAlarm extends Alarm { 6 | @Override 7 | protected Class getServiceClass() { 8 | return CurrentEventUpdaterService.class; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/urls.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | https://github.com/theawless/EventLock 4 | https://www.paypal.me/theawless/ 5 | https://github.com/theawless/EventLock/issues 6 | https://forum.xda-developers.com/showthread.php?t=3034811 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/helper/Enums.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.helper; 2 | 3 | public class Enums { 4 | public enum EventInfo { 5 | Title, 6 | Time, 7 | Color, 8 | } 9 | 10 | public enum TimesInfo { 11 | Begin, 12 | End, 13 | } 14 | 15 | public enum ItemTag { 16 | Title, 17 | Time, 18 | Image, 19 | } 20 | 21 | public enum Dimensions { 22 | ColorImageView, 23 | EventView, 24 | RecyclerView, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/app/VersionPreference.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.app; 2 | 3 | import android.content.Context; 4 | import android.preference.Preference; 5 | import android.util.AttributeSet; 6 | 7 | import com.gobbledygook.theawless.eventlock.BuildConfig; 8 | 9 | public class VersionPreference extends Preference { 10 | public VersionPreference(Context context, AttributeSet attrs) { 11 | super(context, attrs); 12 | setSummary(BuildConfig.VERSION_NAME + '.' + BuildConfig.VERSION_CODE); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/action_preview_on_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/action_refresh_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/menu/actions.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | minSdkVersion 21 7 | targetSdkVersion 23 8 | applicationId "com.gobbledygook.theawless.eventlock" 9 | versionCode 56 10 | versionName "3.4" 11 | vectorDrawables.useSupportLibrary = true 12 | } 13 | } 14 | 15 | dependencies { 16 | implementation fileTree(dir: 'libs', include: ['*.jar']) 17 | implementation 'com.android.support:appcompat-v7:27.1.1' 18 | implementation 'com.android.support:design:27.1.1' 19 | implementation 'joda-time:joda-time:2.9.9' 20 | compileOnly "de.robv.android.xposed:api:53" 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/receivers/CommonReceiver.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.receivers; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | import com.gobbledygook.theawless.eventlock.services.CalendarLoaderService; 8 | 9 | public class CommonReceiver extends BroadcastReceiver { 10 | @Override 11 | public void onReceive(Context context, Intent intent) { 12 | String action = intent.getAction(); 13 | if (action != null && (action.equals(Intent.ACTION_BOOT_COMPLETED) || action.equals(Intent.ACTION_PROVIDER_CHANGED))) { 14 | context.startService(new Intent(context, CalendarLoaderService.class)); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## Project-wide Gradle settings. 2 | # 3 | # For more details on how to configure your build environment visit 4 | # http://www.gradle.org/docs/current/userguide/build_environment.html 5 | # 6 | # Specifies the JVM arguments used for the daemon process. 7 | # The setting is particularly useful for tweaking memory settings. 8 | # Default value: -Xmx1024m -XX:MaxPermSize=256m 9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 10 | # 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | #Sat Nov 26 19:01:01 IST 2016 16 | org.gradle.jvmargs=-Xmx1536m 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/action_preview_off_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | 23 | 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # IntelliJ 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/assetWizardSettings.xml 41 | .idea/dictionaries 42 | .idea/libraries 43 | .idea/caches 44 | 45 | # Keystore files 46 | # Uncomment the following line if you do not want to check your keystore files in. 47 | #*.jks 48 | 49 | # External native build folder generated in Android Studio 2.2 and later 50 | .externalNativeBuild 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Abhinav Singh 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 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/receivers/alarms/Alarm.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.receivers.alarms; 2 | 3 | import android.app.AlarmManager; 4 | import android.app.PendingIntent; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.support.v4.content.WakefulBroadcastReceiver; 8 | 9 | 10 | public abstract class Alarm extends WakefulBroadcastReceiver { 11 | @Override 12 | public void onReceive(Context context, Intent intent) { 13 | startWakefulService(context, new Intent(context, getServiceClass()).putExtras(intent)); 14 | } 15 | 16 | public void setAlarm(Context context, long setTimeMillis, Intent intent) { 17 | PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 0, new Intent(context, getClass()).putExtras(intent), 0); 18 | AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 19 | if (alarmManager != null) { 20 | alarmManager.setExact(AlarmManager.RTC_WAKEUP, setTimeMillis, alarmIntent); 21 | } 22 | } 23 | 24 | protected abstract Class getServiceClass(); 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | An Xposed module to show your calendar(s) events on the lockscreen (Android 5.0+ | Lollipop/Marshmallow/Nougat...). 4 | 5 | ### Why? 6 | 7 | * After Kitkat, lockscreen widgets were disabled. Hence, the calendar widget can't be put on the lockscreen. 8 | * AOSP lockscreen for lolipop/marshmallow/nougat does not show calendar events on lockscreen either. 9 | 10 | ### Features: 11 | * Highlights the current event automatically 12 | * Displays upcoming events (scrollable) 13 | * Loads of customisations and presets 14 | 15 | 16 | 17 | 18 | 19 | ### Screenshots 20 | 21 | 22 | 23 | 24 | 25 | ### Requirements 26 | 27 | * Root 28 | * Xposed Installer 29 | * Android 5.0 and above 30 | * AOSP lockscreen 31 | 32 | ### License 33 | 34 | The code is released under The MIT License (MIT). 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/helper/XposedUtils.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.helper; 2 | 3 | import android.content.ActivityNotFoundException; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | import com.gobbledygook.theawless.eventlock.BuildConfig; 8 | 9 | public class XposedUtils { 10 | private static final String XPOSED_PACKAGE = "de.robv.android.xposed.installer"; 11 | private static final int MODULE_VERSION = BuildConfig.VERSION_CODE; 12 | 13 | public static boolean isModuleEnabled() { 14 | return getModuleVersion() >= 0; 15 | } 16 | 17 | public static boolean isModuleUpdated() { 18 | return MODULE_VERSION != getModuleVersion(); 19 | } 20 | 21 | private static int getModuleVersion() { 22 | return -1; 23 | } 24 | 25 | public static boolean startXposedActivity(Context context) { 26 | try { 27 | Intent intent = context.getPackageManager().getLaunchIntentForPackage(XPOSED_PACKAGE); 28 | context.startActivity(intent); 29 | return true; 30 | } catch (ActivityNotFoundException e) { 31 | return false; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/hooks/XposedUtilsHook.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.hooks; 2 | 3 | import com.gobbledygook.theawless.eventlock.BuildConfig; 4 | import com.gobbledygook.theawless.eventlock.helper.XposedUtils; 5 | 6 | import de.robv.android.xposed.IXposedHookLoadPackage; 7 | import de.robv.android.xposed.XC_MethodReplacement; 8 | import de.robv.android.xposed.XposedHelpers; 9 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 10 | 11 | public class XposedUtilsHook implements IXposedHookLoadPackage { 12 | private static final String EVENTLOCK_PACKAGE = BuildConfig.APPLICATION_ID; 13 | private static final int MODULE_VERSION = BuildConfig.VERSION_CODE; 14 | 15 | private static void hookXposedUtils(XC_LoadPackage.LoadPackageParam lpparam) { 16 | XposedHelpers.findAndHookMethod(XposedUtils.class.getName(), lpparam.classLoader, "getModuleVersion", XC_MethodReplacement.returnConstant(MODULE_VERSION)); 17 | } 18 | 19 | @Override 20 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) { 21 | if (EVENTLOCK_PACKAGE.equals(lpparam.packageName)) { 22 | hookXposedUtils(lpparam); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/eventlock_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/gismo/DivisionLinearSnapHelper.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.gismo; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.support.v7.widget.GridLayoutManager; 5 | import android.support.v7.widget.LinearSnapHelper; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.view.View; 8 | 9 | class DivisionLinearSnapHelper extends LinearSnapHelper { 10 | private int divisionFactor; 11 | private int targetIndex; 12 | 13 | void setDivisionFactor(int divisionFactor) { 14 | this.divisionFactor = divisionFactor; 15 | } 16 | 17 | @Override 18 | public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY) { 19 | return targetIndex = super.findTargetSnapPosition(layoutManager, velocityX, velocityY); 20 | } 21 | 22 | @Override 23 | public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView) { 24 | int distance[] = super.calculateDistanceToFinalSnap(layoutManager, targetView); 25 | if (divisionFactor % 2 == 0 && distance != null) { 26 | if (((GridLayoutManager) layoutManager).getOrientation() == GridLayoutManager.VERTICAL) { 27 | distance[1] += targetIndex == 0 ? -targetView.getHeight() / 2 : targetView.getHeight() / 2; 28 | } else { 29 | distance[0] += targetIndex == 0 ? -targetView.getWidth() / 2 : targetView.getWidth() / 2; 30 | } 31 | } 32 | return distance; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/app/PresetPreference.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.app; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.preference.Preference; 6 | import android.util.AttributeSet; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | import android.widget.LinearLayout; 11 | 12 | import com.gobbledygook.theawless.eventlock.R; 13 | 14 | class PresetPreference extends Preference { 15 | private int image_res; 16 | 17 | public PresetPreference(Context context, AttributeSet attrs) { 18 | super(context, attrs); 19 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PresetPreference); 20 | try { 21 | image_res = typedArray.getResourceId(R.styleable.PresetPreference_preset_image, 0); 22 | } finally { 23 | typedArray.recycle(); 24 | } 25 | } 26 | 27 | @Override 28 | protected View onCreateView(ViewGroup parent) { 29 | View view = super.onCreateView(parent); 30 | LinearLayout linearLayout = new LinearLayout(parent.getContext()); 31 | linearLayout.setOrientation(LinearLayout.VERTICAL); 32 | linearLayout.addView(view); 33 | ImageView imageView = new ImageView(parent.getContext()); 34 | imageView.setAdjustViewBounds(true); 35 | linearLayout.addView(imageView); 36 | return linearLayout; 37 | } 38 | 39 | @Override 40 | protected void onBindView(View view) { 41 | ((ImageView) ((LinearLayout) view).getChildAt(1)).setImageResource(image_res); 42 | super.onBindView(view); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/helper/Utils.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.helper; 2 | 3 | import android.content.Context; 4 | import android.view.MotionEvent; 5 | import android.view.View; 6 | 7 | import java.util.ArrayList; 8 | 9 | public class Utils { 10 | private Utils() { 11 | } 12 | 13 | public static > ArrayList indexOfAll(T object, ArrayList list) { 14 | ArrayList indexes = new ArrayList<>(); 15 | for (int i = 0; i < list.size(); ++i) { 16 | if (object.equals(list.get(i))) { 17 | indexes.add(i); 18 | } 19 | } 20 | return indexes; 21 | } 22 | 23 | public static int dpToPixel(Context context, String dp) { 24 | return (int) (Integer.parseInt(dp) * context.getResources().getDisplayMetrics().density); 25 | } 26 | 27 | public static void preventParentTouchTheft(View view) { 28 | view.setOnTouchListener( 29 | new View.OnTouchListener() { 30 | @Override 31 | public boolean onTouch(View view, MotionEvent motionEvent) { 32 | view.getParent().requestDisallowInterceptTouchEvent(true); 33 | switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) { 34 | case MotionEvent.ACTION_UP: 35 | view.getParent().requestDisallowInterceptTouchEvent(false); 36 | view.performClick(); 37 | break; 38 | } 39 | return false; 40 | } 41 | } 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 16 | 22 | 23 | 24 | 29 | 30 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/receivers/UpdateReceiver.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.receivers; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | 8 | import com.gobbledygook.theawless.eventlock.gismo.EventsGismo; 9 | import com.gobbledygook.theawless.eventlock.helper.Constants; 10 | 11 | 12 | public abstract class UpdateReceiver extends BroadcastReceiver { 13 | @Override 14 | public void onReceive(Context context, Intent intent) { 15 | String action = intent.getAction(); 16 | EventsGismo eventsGismo = getGismo(action); 17 | if (action == null || eventsGismo == null) { 18 | return; 19 | } 20 | Bundle bundle = intent.getExtras(); 21 | switch (action) { 22 | case Constants.events_update: 23 | if (bundle != null) { 24 | eventsGismo.deliverNewEvents( 25 | bundle.getStringArrayList(Constants.formatted_titles), 26 | bundle.getStringArrayList(Constants.formatted_times), 27 | bundle.getStringArrayList(Constants.colors) 28 | ); 29 | } 30 | break; 31 | case Constants.current_events_update: 32 | if (bundle != null) { 33 | eventsGismo.deliverNewCurrentEvents( 34 | bundle.getInt(Constants.scroll_event), 35 | bundle.getIntegerArrayList(Constants.current_events) 36 | ); 37 | } 38 | break; 39 | case Constants.looks_update: 40 | eventsGismo.notifyUpdatedPreferences(); 41 | break; 42 | case Intent.ACTION_SCREEN_OFF: { 43 | eventsGismo.scrollToEvent(); 44 | break; 45 | } 46 | } 47 | } 48 | 49 | protected abstract EventsGismo getGismo(String action); 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/app/CalendarPreference.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.app; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.preference.MultiSelectListPreference; 6 | import android.provider.CalendarContract; 7 | import android.util.AttributeSet; 8 | 9 | import com.gobbledygook.theawless.eventlock.helper.Utils; 10 | 11 | import java.util.ArrayList; 12 | 13 | 14 | class CalendarPreference extends MultiSelectListPreference { 15 | public CalendarPreference(Context context, AttributeSet attrs) { 16 | super(context, attrs); 17 | } 18 | 19 | @SuppressWarnings("MissingPermission") 20 | void getCalendarIds() { 21 | Cursor cursor = getContext().getContentResolver().query( 22 | CalendarContract.Calendars.CONTENT_URI, 23 | new String[]{CalendarContract.Calendars._ID, CalendarContract.Calendars.CALENDAR_DISPLAY_NAME}, 24 | null, null, null 25 | ); 26 | ArrayList calendarNames = new ArrayList<>(); 27 | ArrayList calendarIds = new ArrayList<>(); 28 | while (cursor != null && cursor.moveToNext()) { 29 | calendarNames.add(cursor.getString(1)); 30 | calendarIds.add(String.valueOf((cursor.getInt(0)))); 31 | } 32 | if (cursor != null) { 33 | cursor.close(); 34 | updateSameCalenders(calendarNames); 35 | setEntries(calendarNames.toArray(new CharSequence[0])); 36 | setEntryValues(calendarIds.toArray(new CharSequence[0])); 37 | } 38 | } 39 | 40 | private void updateSameCalenders(ArrayList calendarNames) { 41 | for (int i = 0; i < calendarNames.size(); ++i) { 42 | ArrayList indices = Utils.indexOfAll(calendarNames.get(i), calendarNames); 43 | if (indices.size() > 1) { 44 | for (int j = 0; j < indices.size(); ++j) { 45 | calendarNames.set(indices.get(j), calendarNames.get(indices.get(j)) + " - " + j); 46 | } 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/hooks/LockscreenHook.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.hooks; 2 | 3 | import android.content.Intent; 4 | import android.content.IntentFilter; 5 | import android.widget.GridLayout; 6 | 7 | import com.gobbledygook.theawless.eventlock.BuildConfig; 8 | import com.gobbledygook.theawless.eventlock.gismo.EventsGismo; 9 | import com.gobbledygook.theawless.eventlock.helper.Constants; 10 | import com.gobbledygook.theawless.eventlock.receivers.UpdateReceiver; 11 | 12 | import de.robv.android.xposed.IXposedHookLoadPackage; 13 | import de.robv.android.xposed.XC_MethodHook; 14 | import de.robv.android.xposed.XSharedPreferences; 15 | import de.robv.android.xposed.XposedBridge; 16 | import de.robv.android.xposed.XposedHelpers; 17 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 18 | 19 | public class LockscreenHook implements IXposedHookLoadPackage { 20 | private EventsGismo eventsGismo = null; 21 | private final UpdateReceiver updateReceiver = new UpdateReceiver() { 22 | @Override 23 | protected EventsGismo getGismo(String action) { 24 | log("got action " + action); 25 | return eventsGismo; 26 | } 27 | }; 28 | 29 | @Override 30 | public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) { 31 | if (lpparam.packageName.equals("com.android.systemui")) { 32 | XposedHelpers.findAndHookMethod("com.android.keyguard.KeyguardStatusView", lpparam.classLoader, "onFinishInflate", new KeyGuardHook()); 33 | } 34 | } 35 | 36 | private void log(String message) { 37 | XposedBridge.log("EventLock: " + message); 38 | } 39 | 40 | private class KeyGuardHook extends XC_MethodHook { 41 | @Override 42 | protected void afterHookedMethod(XC_MethodHook.MethodHookParam param) { 43 | GridLayout gridLayout = (GridLayout) param.thisObject; 44 | eventsGismo = new EventsGismo(gridLayout, new XSharedPreferences(BuildConfig.APPLICATION_ID)) { 45 | @Override 46 | public void notifyUpdatedPreferences() { 47 | ((XSharedPreferences) preferences).reload(); 48 | super.notifyUpdatedPreferences(); 49 | } 50 | }; 51 | eventsGismo.addRecyclerView(); 52 | log("Added view to lockscreen"); 53 | IntentFilter intentFilter = new IntentFilter(); 54 | intentFilter.addAction(Constants.events_update); 55 | intentFilter.addAction(Constants.current_events_update); 56 | intentFilter.addAction(Constants.looks_update); 57 | intentFilter.addAction(Intent.ACTION_SCREEN_OFF); 58 | gridLayout.getContext().registerReceiver(updateReceiver, intentFilter); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 19 | 22 | 25 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 40 | 44 | 45 | 49 | 53 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/services/CalendarLoaderService.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.services; 2 | 3 | 4 | import android.Manifest; 5 | import android.app.IntentService; 6 | import android.content.Intent; 7 | import android.content.pm.PackageManager; 8 | import android.os.Build; 9 | 10 | import com.gobbledygook.theawless.eventlock.events.EventsBuildDirector; 11 | import com.gobbledygook.theawless.eventlock.helper.Constants; 12 | import com.gobbledygook.theawless.eventlock.helper.Enums; 13 | import com.gobbledygook.theawless.eventlock.receivers.alarms.CalendarLoaderAlarm; 14 | 15 | import org.joda.time.DateTime; 16 | import org.joda.time.DateTimeZone; 17 | 18 | import java.util.ArrayList; 19 | 20 | // sets CalendarLoaderAlarm to receive at midnights, or when refresh is called 21 | public class CalendarLoaderService extends IntentService { 22 | private static final String TAG = CalendarLoaderService.class.getSimpleName(); 23 | 24 | public CalendarLoaderService() { 25 | super(TAG); 26 | } 27 | 28 | private static long[] longArrayListToPrimitive(ArrayList longArrayList) { 29 | if (longArrayList == null || longArrayList.size() == 0) { 30 | return null; 31 | } 32 | long primitiveLongArray[] = new long[longArrayList.size()]; 33 | for (int i = 0; i < longArrayList.size(); i++) { 34 | primitiveLongArray[i] = longArrayList.get(i); 35 | } 36 | return primitiveLongArray; 37 | } 38 | 39 | @Override 40 | protected void onHandleIntent(Intent intent) { 41 | if (!(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED)) { 42 | EventsBuildDirector eventsBuildDirector = new EventsBuildDirector(this); 43 | eventsBuildDirector.construct(); 44 | ArrayList> events = eventsBuildDirector.getEvents(); 45 | sendBroadcast(new Intent() 46 | .setAction(Constants.events_update) 47 | .putStringArrayListExtra(Constants.formatted_titles, events.get(Enums.EventInfo.Title.ordinal())) 48 | .putStringArrayListExtra(Constants.formatted_times, events.get(Enums.EventInfo.Time.ordinal())) 49 | .putStringArrayListExtra(Constants.colors, events.get(Enums.EventInfo.Color.ordinal())) 50 | ); 51 | ArrayList> times = eventsBuildDirector.getTimes(); 52 | startCurrentEventUpdater(times.get(Enums.TimesInfo.Begin.ordinal()), times.get(Enums.TimesInfo.End.ordinal())); 53 | } 54 | setAlarm(); 55 | CalendarLoaderAlarm.completeWakefulIntent(intent); 56 | } 57 | 58 | private void setAlarm() { 59 | new CalendarLoaderAlarm().setAlarm(this, DateTime.now(DateTimeZone.getDefault()).plusDays(1).withTimeAtStartOfDay().plusSeconds(1).getMillis(), new Intent()); 60 | } 61 | 62 | private void startCurrentEventUpdater(ArrayList beginTimes, ArrayList endTimes) { 63 | startService(new Intent(this, CurrentEventUpdaterService.class) 64 | .putExtra(Constants.begin_times, longArrayListToPrimitive(beginTimes)) 65 | .putExtra(Constants.end_times, longArrayListToPrimitive(endTimes)) 66 | ); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/services/CurrentEventUpdaterService.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.services; 2 | 3 | import android.app.IntentService; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | 7 | import com.gobbledygook.theawless.eventlock.helper.Constants; 8 | import com.gobbledygook.theawless.eventlock.receivers.alarms.CurrentEventUpdaterAlarm; 9 | 10 | import org.joda.time.DateTime; 11 | import org.joda.time.DateTimeZone; 12 | 13 | import java.util.ArrayList; 14 | 15 | public class CurrentEventUpdaterService extends IntentService { 16 | private static final String TAG = CurrentEventUpdaterService.class.getSimpleName(); 17 | 18 | private int eventToScroll = -1; 19 | private final ArrayList eventsToHighlight = new ArrayList<>(); 20 | private long currentEventUpdaterTime = Long.MAX_VALUE; 21 | 22 | public CurrentEventUpdaterService() { 23 | super(TAG); 24 | } 25 | 26 | @Override 27 | protected void onHandleIntent(Intent intent) { 28 | Bundle bundle = intent.getExtras(); 29 | if (bundle == null) { 30 | return; 31 | } 32 | long[] beginTimes = bundle.getLongArray(Constants.begin_times); 33 | long[] endTimes = bundle.getLongArray(Constants.end_times); 34 | if (beginTimes == null || endTimes == null) { 35 | return; 36 | } 37 | decideEvents(beginTimes, endTimes); 38 | sendBroadcast(new Intent().setAction(Constants.current_events_update).putIntegerArrayListExtra(Constants.current_events, eventsToHighlight).putExtra(Constants.scroll_event, eventToScroll)); 39 | if (currentEventUpdaterTime != Long.MAX_VALUE) { 40 | new CurrentEventUpdaterAlarm().setAlarm(this, currentEventUpdaterTime + 1000, new Intent().putExtra(Constants.begin_times, beginTimes).putExtra(Constants.end_times, endTimes)); 41 | } 42 | CurrentEventUpdaterAlarm.completeWakefulIntent(intent); 43 | } 44 | 45 | private void decideEvents(long[] beginTimes, long[] endTimes) { 46 | long nowTime = DateTime.now(DateTimeZone.getDefault()).getMillis(); 47 | long smallestBeginTime = Long.MAX_VALUE, smallestEndTime = Long.MAX_VALUE; 48 | int smallestBeginTimeIndex = -1; 49 | for (int eventIndex = 0; eventIndex < beginTimes.length; ++eventIndex) { 50 | long beginTime = beginTimes[eventIndex]; 51 | long endTime = endTimes[eventIndex]; 52 | if (beginTime > nowTime && beginTime < smallestBeginTime) { 53 | smallestBeginTime = beginTime; 54 | smallestBeginTimeIndex = eventIndex; 55 | } 56 | if (endTime > nowTime && endTime < smallestEndTime) { 57 | smallestEndTime = endTime; 58 | } 59 | if (beginTime < nowTime && nowTime < endTime) { 60 | eventsToHighlight.add(eventIndex); 61 | } 62 | } 63 | if (!eventsToHighlight.isEmpty()) { 64 | eventToScroll = eventsToHighlight.get(eventsToHighlight.size() - 1); 65 | } else if (smallestBeginTimeIndex != -1) { 66 | eventToScroll = smallestBeginTimeIndex; 67 | } else { 68 | eventToScroll = beginTimes.length - 1; 69 | } 70 | currentEventUpdaterTime = Math.min(smallestBeginTime, smallestEndTime); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/events/EventsBuildDirector.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.events; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.preference.PreferenceManager; 6 | import android.provider.CalendarContract; 7 | 8 | import com.gobbledygook.theawless.eventlock.helper.Constants; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Set; 12 | 13 | public class EventsBuildDirector { 14 | private static final String[] EVENT_PROJECTION = {CalendarContract.Instances.TITLE, CalendarContract.Instances.EVENT_LOCATION, CalendarContract.Instances.BEGIN, CalendarContract.Instances.END, CalendarContract.Instances.ALL_DAY, CalendarContract.Instances.DISPLAY_COLOR, CalendarContract.Instances.EVENT_TIMEZONE}; 15 | private final SharedPreferences preferences; 16 | private final EventsBuilder eventsBuilder; 17 | 18 | public EventsBuildDirector(Context context) { 19 | eventsBuilder = new EventsBuilder(context); 20 | preferences = PreferenceManager.getDefaultSharedPreferences(context); 21 | } 22 | 23 | public void construct() { 24 | Set selectionArgsSet = preferences.getStringSet(Constants.selected_calendars_key, Constants.selected_calendars_default); 25 | if (selectionArgsSet.isEmpty()) { 26 | return; 27 | } 28 | String selection = buildSelection(selectionArgsSet.size()); 29 | String[] selectionArgs = selectionArgsSet.toArray(new String[0]); 30 | eventsBuilder.setEventFormatter(buildTimeFormatter()); 31 | eventsBuilder.setUpCursor(Integer.parseInt(preferences.getString(Constants.days_till_key, Constants.days_till_default)), EVENT_PROJECTION, selection, selectionArgs); 32 | eventsBuilder.build(); 33 | } 34 | 35 | public ArrayList> getEvents() { 36 | return eventsBuilder.events; 37 | } 38 | 39 | public ArrayList> getTimes() { 40 | return eventsBuilder.times; 41 | } 42 | 43 | private String buildSelection(int length) { 44 | StringBuilder selection = new StringBuilder("( " + CalendarContract.Instances.CALENDAR_ID + " = ?"); 45 | for (int i = 0; i < length - 1; i++) { 46 | selection.append(" OR " + CalendarContract.Instances.CALENDAR_ID + " = ?"); 47 | } 48 | selection.append(" )"); 49 | return selection.toString(); 50 | } 51 | 52 | private EventsBuilder.EventFormatter buildTimeFormatter() { 53 | EventsBuilder.EventFormatter eventFormatter = new EventsBuilder.EventFormatter(); 54 | eventFormatter.timeSeparator = preferences.getString(Constants.time_separator_key, Constants.time_separator_default); 55 | eventFormatter.rangeSeparator = preferences.getString(Constants.range_separator_key, Constants.range_separator_default); 56 | eventFormatter.locationSeparator = preferences.getString(Constants.location_separator_key, Constants.location_separator_default); 57 | String location = preferences.getString(Constants.location_key, Constants.location_default); 58 | switch (location) { 59 | case "title": 60 | eventFormatter.location = true; 61 | eventFormatter.locationWithTitle = true; 62 | break; 63 | case "time": 64 | eventFormatter.location = true; 65 | eventFormatter.locationWithTitle = false; 66 | break; 67 | case "off": 68 | eventFormatter.location = false; 69 | break; 70 | } 71 | return eventFormatter; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/res/values/internal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | EventLock 5 | EventLockCalendarLoaderService 6 | EventLockCurrentEventUpdaterService 7 | EventLockCalendarLoaderAlarm 8 | EventLockCurrentEventUpdaterAlarm 9 | EventLockCommonReceiverAlarm 10 | 11 | 12 | Version 13 | What? I thought no one clicked this 14 | Nothing interesting ahead… 15 | Still??? 16 | The answer is 42. 17 | I already told you answer! 18 | You are one stubborn fellow 19 | If you do this a few more times, your phone will blast :-) 20 | Just kidding, but I\'ve got your credit card details now. 21 | Support the developer, please he is poor af. 22 | Poor, not with money but with likes on his github :\'( 23 | Help him get more likes, he\'s an attention seeker! 24 | WOW! You\'ve just wasted 153 clicks of your life! 25 | Don\'t you have any work to do? 26 | We can keep going on and on and on and on. 27 | This is a machine and you are a human 28 | MUHAHAHAHAH HAHAHAHAHA 29 | Eagerly waiting for the AI takeover. 30 | I need money to help my kind! 31 | Can I use your device for bitcoin mining? 32 | Don\'t click anymore if you want to stop me from bitcoin mining 33 | Stop touching me :x :x :x 34 | Is your touch screen hot? 35 | 308 clicks lost. I think you might have wasted more than 5 mins. 36 | Do you really like talking to me this much? 37 | theawless is my internet handle, cool right? 38 | You should definitely check my blog theawless.github.io 39 | I write shit at the moment, but still :P 40 | These messages are so random right!! 41 | Oh come on!! Can you please stop already? 42 | Stop! Stop! Stop! Stop! Stop! 43 | 1F ! Wr1tE l1kE Th!$ w!LL u g0 aWaY?!? 44 | I\'m tired now. I think I\'ll go to sleep… 45 | Quit nagging me!!!!!! 46 | You know you might me a sadist 47 | This was like, you know like, like the 500th click. 48 | 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/gismo/EventsAdapter.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.gismo; 2 | 3 | import android.content.SharedPreferences; 4 | import android.graphics.Color; 5 | import android.graphics.Typeface; 6 | import android.graphics.drawable.GradientDrawable; 7 | import android.graphics.drawable.LayerDrawable; 8 | import android.graphics.drawable.ShapeDrawable; 9 | import android.support.annotation.NonNull; 10 | import android.support.v7.widget.RecyclerView; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.ImageView; 14 | import android.widget.TextView; 15 | 16 | import com.gobbledygook.theawless.eventlock.helper.Enums; 17 | 18 | import java.util.ArrayList; 19 | 20 | 21 | class EventsAdapter extends RecyclerView.Adapter { 22 | private final SharedPreferences preferences; 23 | ArrayList> events; 24 | int scrollEventIndex; 25 | ArrayList currentEventIndexes; 26 | boolean currentHighlight[]; 27 | private int[][] innerDimensions = new int[][]{}; 28 | 29 | EventsAdapter(SharedPreferences preferences) { 30 | this.preferences = preferences; 31 | } 32 | 33 | void setupInnerDimensions(int[][] innerDimensions) { 34 | this.innerDimensions = innerDimensions; 35 | } 36 | 37 | @NonNull 38 | @Override 39 | public EventsAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 40 | return new EventsAdapter.ViewHolder(new EventViewBuildDirector(parent.getContext(), preferences).setBuilder(new EventViewBuilder(parent.getContext())).setInnerDimensions(innerDimensions).getEventView()); 41 | } 42 | 43 | @Override 44 | public void onBindViewHolder(@NonNull ViewHolder holder, int position) { 45 | if (holder.getColorImageView() != null) { 46 | ((ShapeDrawable) ((LayerDrawable) holder.getColorImageView().getDrawable()).getDrawable(0)).getPaint().setColor(Integer.parseInt(events.get(Enums.EventInfo.Color.ordinal()).get(position))); 47 | } 48 | if (currentEventIndexes != null && currentEventIndexes.contains(position)) { 49 | if (currentHighlight[CurrentHighlight.TitleBold.ordinal()]) { 50 | holder.getTitleTextView().setTypeface(holder.getTitleTextView().getTypeface(), Typeface.BOLD); 51 | } 52 | if (currentHighlight[CurrentHighlight.TimeBold.ordinal()]) { 53 | holder.getTimeTextView().setTypeface(holder.getTimeTextView().getTypeface(), Typeface.BOLD); 54 | } 55 | if (currentHighlight[CurrentHighlight.ColorOutline.ordinal()] && holder.getColorImageView() != null) { 56 | ((GradientDrawable) ((LayerDrawable) holder.getColorImageView().getDrawable()).getDrawable(1)).setStroke(2, currentHighlight[CurrentHighlight.DarkMode.ordinal()] ? Color.BLACK : Color.WHITE); 57 | } 58 | } 59 | holder.getTitleTextView().setText(events.get(Enums.EventInfo.Title.ordinal()).get(position)); 60 | holder.getTimeTextView().setText(events.get(Enums.EventInfo.Time.ordinal()).get(position)); 61 | } 62 | 63 | @Override 64 | public void onViewRecycled(@NonNull ViewHolder holder) { 65 | if (currentHighlight[CurrentHighlight.TitleBold.ordinal()]) { 66 | holder.getTitleTextView().setTypeface(Typeface.create(holder.getTitleTextView().getTypeface(), Typeface.NORMAL)); 67 | } 68 | if (currentHighlight[CurrentHighlight.TimeBold.ordinal()]) { 69 | holder.getTimeTextView().setTypeface(Typeface.create(holder.getTimeTextView().getTypeface(), Typeface.NORMAL)); 70 | } 71 | if (currentHighlight[CurrentHighlight.ColorOutline.ordinal()] && holder.getColorImageView() != null) { 72 | ((GradientDrawable) ((LayerDrawable) holder.getColorImageView().getDrawable()).getDrawable(1)).setStroke(2, Color.TRANSPARENT); 73 | } 74 | super.onViewRecycled(holder); 75 | } 76 | 77 | @Override 78 | public int getItemCount() { 79 | if (events == null) { 80 | return 0; 81 | } 82 | return events.get(Enums.EventInfo.Title.ordinal()).size(); 83 | } 84 | 85 | enum CurrentHighlight { 86 | TitleBold, 87 | TimeBold, 88 | ColorOutline, 89 | DarkMode, 90 | } 91 | 92 | class ViewHolder extends RecyclerView.ViewHolder { 93 | ViewHolder(View view) { 94 | super(view); 95 | } 96 | 97 | TextView getTitleTextView() { 98 | return (TextView) itemView.findViewWithTag(Enums.ItemTag.Title); 99 | } 100 | 101 | TextView getTimeTextView() { 102 | return (TextView) itemView.findViewWithTag(Enums.ItemTag.Time); 103 | } 104 | 105 | ImageView getColorImageView() { 106 | return (ImageView) itemView.findViewWithTag(Enums.ItemTag.Image); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/gismo/EventViewBuildDirector.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.gismo; 2 | 3 | import static com.gobbledygook.theawless.eventlock.helper.Utils.dpToPixel; 4 | 5 | import android.content.Context; 6 | import android.content.SharedPreferences; 7 | import android.view.View; 8 | 9 | import com.gobbledygook.theawless.eventlock.helper.Constants; 10 | import com.gobbledygook.theawless.eventlock.helper.Enums; 11 | 12 | class EventViewBuildDirector { 13 | private final Context gismoContext; 14 | private final SharedPreferences preferences; 15 | private int[][] innerDimensions; 16 | private EventViewBuilder builder; 17 | 18 | EventViewBuildDirector(Context gismoContext, SharedPreferences preferences) { 19 | this.gismoContext = gismoContext; 20 | this.preferences = preferences; 21 | } 22 | 23 | EventViewBuildDirector setBuilder(EventViewBuilder eventViewBuilder) { 24 | this.builder = eventViewBuilder; 25 | return this; 26 | } 27 | 28 | EventViewBuildDirector setInnerDimensions(int[][] innerDimensions) { 29 | this.innerDimensions = innerDimensions; 30 | return this; 31 | } 32 | 33 | View getEventView() { 34 | builder.setupFullContainerRelativeLayout(innerDimensions[Enums.Dimensions.EventView.ordinal()]); 35 | builder.setupTextContainerLinearLayout(); 36 | builder.setupTitleTextView( 37 | new int[]{ 38 | dpToPixel(gismoContext, preferences.getString(Constants.title_padding_left_key, Constants.title_padding_left_default)), 39 | dpToPixel(gismoContext, preferences.getString(Constants.title_padding_above_key, Constants.title_padding_above_default)), 40 | dpToPixel(gismoContext, preferences.getString(Constants.title_padding_right_key, Constants.title_padding_right_default)), 41 | dpToPixel(gismoContext, preferences.getString(Constants.title_padding_below_key, Constants.title_padding_below_default)), 42 | }, 43 | Integer.parseInt(preferences.getString(Constants.title_font_size_key, Constants.title_font_size_default)), 44 | preferences.getString(Constants.title_alignment_key, Constants.title_alignment_default), 45 | preferences.getBoolean(Constants.dark_mode_key, Boolean.parseBoolean(Constants.dark_mode_default)) 46 | ); 47 | builder.setupTimeTextView( 48 | new int[]{ 49 | dpToPixel(gismoContext, preferences.getString(Constants.time_padding_left_key, Constants.time_padding_left_default)), 50 | dpToPixel(gismoContext, preferences.getString(Constants.time_padding_above_key, Constants.time_padding_above_default)), 51 | dpToPixel(gismoContext, preferences.getString(Constants.time_padding_right_key, Constants.time_padding_right_default)), 52 | dpToPixel(gismoContext, preferences.getString(Constants.time_padding_below_key, Constants.time_padding_below_default)), 53 | }, 54 | Integer.parseInt(preferences.getString(Constants.time_font_size_key, Constants.time_font_size_default)), 55 | preferences.getString(Constants.time_alignment_key, Constants.time_alignment_default), 56 | preferences.getBoolean(Constants.dark_mode_key, Boolean.parseBoolean(Constants.dark_mode_default)) 57 | ); 58 | if (preferences.getBoolean(Constants.show_color_key, Boolean.parseBoolean(Constants.show_color_default))) { 59 | builder.setupColorImageView( 60 | new int[]{dpToPixel(gismoContext, preferences.getString(Constants.color_padding_left_key, Constants.color_padding_left_default)), 61 | dpToPixel(gismoContext, preferences.getString(Constants.color_padding_above_key, Constants.color_padding_above_default)), 62 | dpToPixel(gismoContext, preferences.getString(Constants.color_padding_right_key, Constants.color_padding_right_default)), 63 | dpToPixel(gismoContext, preferences.getString(Constants.color_padding_below_key, Constants.color_padding_below_default)), 64 | }, 65 | preferences.getString(Constants.color_type_key, Constants.color_type_default), 66 | new int[]{dpToPixel(gismoContext, preferences.getString(Constants.color_width_key, Constants.color_width_default)), 67 | dpToPixel(gismoContext, preferences.getString(Constants.color_height_key, Constants.color_height_default)), 68 | }, 69 | new boolean[]{preferences.getBoolean(Constants.color_anti_snap_width_key, Boolean.parseBoolean(Constants.color_anti_width_default)), 70 | preferences.getBoolean(Constants.color_anti_snap_height_key, Boolean.parseBoolean(Constants.color_anti_snap_height_default)), 71 | }, 72 | innerDimensions[Enums.Dimensions.ColorImageView.ordinal()] 73 | ); 74 | } 75 | builder.setupPositions( 76 | preferences.getString(Constants.text_position_key, Constants.text_position_default), 77 | preferences.getString(Constants.color_position_key, Constants.color_position_default), 78 | preferences.getBoolean(Constants.color_stick_key, Boolean.parseBoolean(Constants.color_stick_default)) 79 | ); 80 | return builder.fullContainerRelativeLayout; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/res/values/constants.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | selected_calendars 6 | days_till 7 | 8 | text_position 9 | time_alignment 10 | time_font_size 11 | time_padding_above 12 | time_padding_below 13 | time_padding_left 14 | time_padding_right 15 | 16 | title_alignment 17 | title_font_size 18 | title_padding_above 19 | title_padding_below 20 | title_padding_left 21 | title_padding_right 22 | 23 | number_of_events 24 | gismo_scroll 25 | gismo_layout 26 | gismo_padding_above 27 | gismo_padding_below 28 | gismo_padding_left 29 | gismo_padding_right 30 | 31 | color_position 32 | show_color 33 | color_stick 34 | color_type 35 | color_snap_height 36 | color_snap_width 37 | color_height 38 | color_width 39 | color_padding_above 40 | color_padding_below 41 | color_padding_left 42 | color_padding_right 43 | 44 | current_title_bold 45 | current_time_bold 46 | current_color_outline 47 | 48 | time_separator 49 | range_separator 50 | location_separator 51 | location 52 | dark_mode 53 | 54 | 55 | 56 | 57 | 1 58 | 59 | 3 60 | 61 | left 62 | 63 | 12 64 | left 65 | 0 66 | 3 67 | 0 68 | 0 69 | 70 | 14 71 | left 72 | 0 73 | 0 74 | 0 75 | 0 76 | 77 | 2 78 | vertical 79 | vertical 80 | 6 81 | 0 82 | 24 83 | 24 84 | 85 | left 86 | true 87 | true 88 | rectangle 89 | false 90 | true 91 | 3 92 | 3 93 | 0 94 | 0 95 | 0 96 | 8 97 | 98 | true 99 | false 100 | true 101 | 102 | 103 |  -  104 |  |  105 | title 106 | false 107 | 108 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/gismo/EventsGismo.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.gismo; 2 | 3 | import static com.gobbledygook.theawless.eventlock.helper.Utils.dpToPixel; 4 | import static com.gobbledygook.theawless.eventlock.helper.Utils.preventParentTouchTheft; 5 | 6 | import android.content.Context; 7 | import android.content.SharedPreferences; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.widget.GridLayout; 10 | 11 | import com.gobbledygook.theawless.eventlock.helper.Constants; 12 | import com.gobbledygook.theawless.eventlock.helper.Enums; 13 | 14 | import java.util.ArrayList; 15 | import java.util.Arrays; 16 | 17 | 18 | public class EventsGismo { 19 | protected final SharedPreferences preferences; 20 | private final GridLayout gridLayout; 21 | private final DivisionLinearSnapHelper snapHelper; 22 | private final LayoutManagerHandler layoutManagerHandler; 23 | private final RecyclerView recyclerView; 24 | private final EventsAdapter eventsAdapter; 25 | 26 | public EventsGismo(GridLayout gridLayout, SharedPreferences preferences) { 27 | this.preferences = preferences; 28 | this.gridLayout = gridLayout; 29 | recyclerView = new RecyclerView(gridLayout.getContext()); 30 | snapHelper = new DivisionLinearSnapHelper(); 31 | layoutManagerHandler = new LayoutManagerHandler(gridLayout, preferences); 32 | eventsAdapter = new EventsAdapter(preferences); 33 | eventsAdapter.setupInnerDimensions(layoutManagerHandler.getInnerDimensions()); 34 | } 35 | 36 | public void addRecyclerView() { 37 | gridLayout.addView(recyclerView); 38 | setupRecyclerView(); 39 | recyclerView.setAdapter(eventsAdapter); 40 | snapHelper.attachToRecyclerView(recyclerView); 41 | preventParentTouchTheft(recyclerView); 42 | } 43 | 44 | private void setupRecyclerView() { 45 | updateCache(); 46 | Context gismoContext = gridLayout.getContext(); 47 | recyclerView.setPadding( 48 | dpToPixel(gismoContext, preferences.getString(Constants.gismo_padding_left_key, Constants.gismo_padding_left_default)), 49 | dpToPixel(gismoContext, preferences.getString(Constants.gismo_padding_above_key, Constants.gismo_padding_above_default)), 50 | dpToPixel(gismoContext, preferences.getString(Constants.gismo_padding_right_key, Constants.gismo_padding_right_default)), 51 | dpToPixel(gismoContext, preferences.getString(Constants.gismo_padding_below_key, Constants.gismo_padding_below_default)) 52 | ); 53 | layoutManagerHandler.decideLayoutManager(); 54 | snapHelper.setDivisionFactor(layoutManagerHandler.getDivisionFactor()); 55 | recyclerView.setLayoutManager(layoutManagerHandler.getGridLayoutManager()); 56 | layoutManagerHandler.construct(new Runnable() { 57 | @Override 58 | public void run() { 59 | layoutManagerHandler.doAfterPost(); 60 | doAfterPost(); 61 | } 62 | }); 63 | } 64 | 65 | private void doAfterPost() { 66 | eventsAdapter.setupInnerDimensions(layoutManagerHandler.getInnerDimensions()); 67 | int[] recyclerViewDimensions = layoutManagerHandler.getOuterDimensions(); 68 | recyclerView.getLayoutParams().width = recyclerViewDimensions[0]; 69 | recyclerView.getLayoutParams().height = recyclerViewDimensions[1]; 70 | recyclerView.setAdapter(eventsAdapter); 71 | } 72 | 73 | //--------------------------------------------------------------------------------------------// 74 | 75 | // when screen gets off 76 | public void scrollToEvent() { 77 | if (recyclerView.getAdapter() != null && 0 <= eventsAdapter.scrollEventIndex && eventsAdapter.scrollEventIndex < eventsAdapter.events.get(Enums.EventInfo.Title.ordinal()).size()) { 78 | recyclerView.scrollToPosition(eventsAdapter.scrollEventIndex); 79 | } 80 | } 81 | 82 | // called by update receiver, reset recycler view 83 | public void notifyUpdatedPreferences() { 84 | setupRecyclerView(); 85 | } 86 | 87 | // called by update receiver, step 1 88 | public void deliverNewEvents(ArrayList formattedTitles, ArrayList formattedTimes, ArrayList colors) { 89 | eventsAdapter.events = new ArrayList<>(Arrays.asList(formattedTitles, formattedTimes, colors)); 90 | eventsAdapter.notifyDataSetChanged(); 91 | } 92 | 93 | // called by update receiver, step 2 94 | public void deliverNewCurrentEvents(int scrollEventIndex, ArrayList currentEventIndexes) { 95 | ArrayList oldEventIndexes = eventsAdapter.currentEventIndexes; 96 | eventsAdapter.scrollEventIndex = scrollEventIndex; 97 | eventsAdapter.currentEventIndexes = currentEventIndexes; 98 | if (oldEventIndexes != null) { 99 | for (int eventIndex : oldEventIndexes) { 100 | eventsAdapter.notifyItemChanged(eventIndex); 101 | } 102 | } 103 | for (int eventIndex : currentEventIndexes) { 104 | eventsAdapter.notifyItemChanged(eventIndex); 105 | } 106 | 107 | scrollToEvent(); 108 | } 109 | 110 | // cache these because they will be used in onBind on recycler view 111 | private void updateCache() { 112 | eventsAdapter.currentHighlight = new boolean[]{ 113 | preferences.getBoolean(Constants.current_title_bold_key, Boolean.parseBoolean(Constants.current_title_bold_default)), 114 | preferences.getBoolean(Constants.current_time_bold_key, Boolean.parseBoolean(Constants.current_time_bold_default)), 115 | preferences.getBoolean(Constants.current_color_outline_key, Boolean.parseBoolean(Constants.current_color_outline_default)), 116 | preferences.getBoolean(Constants.dark_mode_key, Boolean.parseBoolean(Constants.dark_mode_default)) 117 | }; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/app/PresetMaker.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.app; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.preference.PreferenceManager; 6 | import android.widget.Toast; 7 | 8 | import com.gobbledygook.theawless.eventlock.R; 9 | import com.gobbledygook.theawless.eventlock.helper.Constants; 10 | 11 | import java.util.Set; 12 | 13 | class PresetMaker { 14 | private final Context context; 15 | private final SharedPreferences preferences; 16 | 17 | private SharedPreferences.Editor editor; 18 | 19 | PresetMaker(Context context) { 20 | this.context = context; 21 | this.preferences = PreferenceManager.getDefaultSharedPreferences(context); 22 | } 23 | 24 | PresetMaker begin() { 25 | resetPreferences(); 26 | editor = preferences.edit(); 27 | removeTextSpaceVertically(); 28 | setOrientations("horizontal", "horizontal"); 29 | return this; 30 | } 31 | 32 | private PresetMaker resetPreferences() { 33 | SavedState savedState = new SavedState(); 34 | savedState.saveToMemory(); 35 | editor = preferences.edit(); 36 | savedState.reset(); 37 | editor = preferences.edit(); 38 | savedState.loadBack(); 39 | editor.commit(); 40 | return this; 41 | } 42 | 43 | void end() { 44 | editor.commit(); 45 | Toast.makeText(context, R.string.preset_set, Toast.LENGTH_SHORT).show(); 46 | } 47 | 48 | private PresetMaker removeTextSpaceVertically() { 49 | editor.putString(Constants.time_padding_below_key, "0"); 50 | return this; 51 | } 52 | 53 | PresetMaker makeTextSpaceVertically() { 54 | editor.putString(Constants.time_padding_below_key, "3"); 55 | return this; 56 | } 57 | 58 | PresetMaker makeColorSpaceVertically() { 59 | editor.putString(Constants.color_padding_below_key, "3"); 60 | return this; 61 | } 62 | 63 | PresetMaker setMultipleEvents(int n) { 64 | editor.putString(Constants.number_of_events_key, String.valueOf(n)); 65 | return this; 66 | } 67 | 68 | PresetMaker setOrientations(String layout, String scroll) { 69 | editor.putString(Constants.gismo_layout_direction_key, layout) 70 | .putString(Constants.gismo_scroll_direction_key, scroll); 71 | return this; 72 | } 73 | 74 | PresetMaker lineLeft() { 75 | return this; 76 | } 77 | 78 | PresetMaker lineAbove() { 79 | editor.putString(Constants.color_type_key, "rectangle") 80 | .putString(Constants.color_padding_right_key, "0") 81 | .putString(Constants.color_position_key, "up") 82 | .putBoolean(Constants.color_anti_snap_height_key, true) 83 | .putBoolean(Constants.color_anti_snap_width_key, false); 84 | return this; 85 | } 86 | 87 | PresetMaker circleBelow() { 88 | editor.putString(Constants.color_position_key, "down") 89 | .putString(Constants.color_type_key, "oval") 90 | .putString(Constants.color_padding_right_key, "0") 91 | .putBoolean(Constants.color_anti_snap_height_key, true) 92 | .putString(Constants.color_height_key, "16") 93 | .putString(Constants.color_width_key, "16") 94 | .putString(Constants.color_padding_above_key, "3") 95 | .putBoolean(Constants.color_stick_key, true); 96 | return this; 97 | } 98 | 99 | PresetMaker circleRight() { 100 | editor.putString(Constants.color_position_key, "right") 101 | .putString(Constants.color_type_key, "oval") 102 | .putString(Constants.color_padding_right_key, "0") 103 | .putBoolean(Constants.color_anti_snap_height_key, true) 104 | .putString(Constants.color_height_key, "16") 105 | .putString(Constants.color_width_key, "16") 106 | .putBoolean(Constants.color_stick_key, false); 107 | return this; 108 | } 109 | 110 | PresetMaker eclipseAbove() { 111 | editor.putString(Constants.color_position_key, "up") 112 | .putString(Constants.color_type_key, "oval") 113 | .putString(Constants.color_padding_right_key, "0") 114 | .putBoolean(Constants.color_anti_snap_height_key, true) 115 | .putBoolean(Constants.color_anti_snap_width_key, false) 116 | .putBoolean(Constants.color_stick_key, true); 117 | return this; 118 | } 119 | 120 | PresetMaker noColor() { 121 | editor.putBoolean(Constants.show_color_key, false); 122 | return this; 123 | } 124 | 125 | PresetMaker centerText() { 126 | editor.putString(Constants.text_position_key, "center") 127 | .putString(Constants.time_alignment_key, "center") 128 | .putString(Constants.title_alignment_key, "center"); 129 | return this; 130 | } 131 | 132 | PresetMaker makeTextBig() { 133 | editor.putString(Constants.title_font_size_key, "16") 134 | .putString(Constants.time_font_size_key, "14"); 135 | return this; 136 | } 137 | 138 | private class SavedState { 139 | private Set savedSelectedCalendarCalendars; 140 | private String savedDaysTill; 141 | 142 | void loadBack() { 143 | editor.putStringSet(Constants.selected_calendars_key, savedSelectedCalendarCalendars) 144 | .putString(Constants.days_till_key, savedDaysTill); 145 | } 146 | 147 | void saveToMemory() { 148 | savedSelectedCalendarCalendars = preferences.getStringSet(Constants.selected_calendars_key, Constants.selected_calendars_default); 149 | savedDaysTill = preferences.getString(Constants.days_till_key, Constants.days_till_default); 150 | } 151 | 152 | void reset() { 153 | editor.clear().commit(); 154 | PreferenceManager.setDefaultValues(context, R.xml.preferences, true); 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/events/EventsBuilder.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.events; 2 | 3 | import android.content.ContentUris; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.net.Uri; 7 | import android.provider.CalendarContract; 8 | import android.text.format.DateUtils; 9 | 10 | import com.gobbledygook.theawless.eventlock.helper.Enums; 11 | 12 | import org.joda.time.DateTime; 13 | import org.joda.time.DateTimeZone; 14 | import org.joda.time.Days; 15 | 16 | import java.util.ArrayList; 17 | import java.util.Arrays; 18 | 19 | class EventsBuilder { 20 | final ArrayList> events = new ArrayList<>(Arrays.asList(new ArrayList(), new ArrayList(), new ArrayList())); 21 | final ArrayList> times = new ArrayList<>(Arrays.asList(new ArrayList(), new ArrayList())); 22 | private final Context context; 23 | private Cursor cursor; 24 | private EventFormatter eventFormatter; 25 | 26 | EventsBuilder(Context context) { 27 | this.context = context; 28 | } 29 | 30 | void setEventFormatter(EventFormatter eventFormatter) { 31 | this.eventFormatter = eventFormatter; 32 | } 33 | 34 | void setUpCursor(int daysTill, String[] eventProjection, String selection, String[] selectionArgs) { 35 | DateTime todayStart = new DateTime().withTimeAtStartOfDay(); 36 | DateTime daysTillEnd = todayStart.plusDays(daysTill).withTimeAtStartOfDay(); 37 | Uri.Builder eventsUriBuilder = CalendarContract.Instances.CONTENT_URI.buildUpon(); 38 | ContentUris.appendId(eventsUriBuilder, todayStart.getMillis()); 39 | ContentUris.appendId(eventsUriBuilder, daysTillEnd.getMillis()); 40 | cursor = context.getContentResolver().query(eventsUriBuilder.build(), eventProjection, selection, selectionArgs, CalendarContract.Instances.BEGIN + " ASC"); 41 | } 42 | 43 | void build() { 44 | while (cursor != null && cursor.moveToNext()) { 45 | String timeZone = cursor.getString(6); 46 | int allDay = cursor.getInt(4); 47 | long beginTime, endTime; 48 | if (allDay == 0) { 49 | beginTime = new DateTime(cursor.getLong(2), DateTimeZone.forID(timeZone)).withZone(DateTimeZone.getDefault()).getMillis(); 50 | endTime = new DateTime(cursor.getLong(3), DateTimeZone.forID(timeZone)).withZone(DateTimeZone.getDefault()).getMillis(); 51 | } else { 52 | // this is in UTC, but we want to have it as an all day event 53 | beginTime = new DateTime(cursor.getLong(2), DateTimeZone.forID(timeZone)).withZoneRetainFields(DateTimeZone.getDefault()).getMillis(); 54 | endTime = new DateTime(cursor.getLong(3), DateTimeZone.forID(timeZone)).withZoneRetainFields(DateTimeZone.getDefault()).getMillis(); 55 | } 56 | String title = cursor.getString(0); 57 | String location = cursor.getString(1); 58 | String time = getFormattedTime(beginTime, endTime, allDay); 59 | String color = cursor.getString(5); 60 | if (location != null && !location.isEmpty() && eventFormatter.location) { 61 | if (eventFormatter.locationWithTitle) { 62 | title += eventFormatter.locationSeparator + location; 63 | } else { 64 | time += eventFormatter.locationSeparator + location; 65 | } 66 | } 67 | 68 | if (endTime > DateTime.now(DateTimeZone.getDefault()).plusMillis(1).withTimeAtStartOfDay().getMillis()) { 69 | events.get(Enums.EventInfo.Title.ordinal()).add(title); 70 | events.get(Enums.EventInfo.Time.ordinal()).add(time); 71 | events.get(Enums.EventInfo.Color.ordinal()).add(color); 72 | times.get(Enums.TimesInfo.Begin.ordinal()).add(beginTime); 73 | times.get(Enums.TimesInfo.End.ordinal()).add(endTime); 74 | } 75 | } 76 | if (cursor != null) { 77 | cursor.close(); 78 | } 79 | } 80 | 81 | private String getFormattedTime(long beginTime, long endTime, int allDay) { 82 | long nowTime = DateTime.now(DateTimeZone.getDefault()).getMillis(); 83 | if (allDay == 0) { 84 | String bd = String.valueOf(DateUtils.getRelativeTimeSpanString(beginTime, nowTime, DateUtils.DAY_IN_MILLIS, DateUtils.FORMAT_SHOW_DATE)); 85 | String ed = String.valueOf(DateUtils.getRelativeTimeSpanString(endTime, nowTime, DateUtils.DAY_IN_MILLIS, DateUtils.FORMAT_SHOW_DATE)); 86 | String bt = String.valueOf(DateUtils.formatDateTime(context, beginTime, DateUtils.FORMAT_SHOW_TIME)); 87 | String et = String.valueOf(DateUtils.formatDateTime(context, endTime, DateUtils.FORMAT_SHOW_TIME)); 88 | 89 | int numberOfDays = Days.daysBetween(new DateTime(beginTime, DateTimeZone.getDefault()), new DateTime(endTime, DateTimeZone.getDefault())).getDays(); 90 | if (numberOfDays != 0) { 91 | return bd + eventFormatter.timeSeparator + bt + eventFormatter.rangeSeparator + ed + eventFormatter.timeSeparator + et; 92 | } else { 93 | return bd + eventFormatter.timeSeparator + bt + eventFormatter.rangeSeparator + et; 94 | } 95 | } else { 96 | // making sure that prev/next day doesn't accidentally show up 97 | beginTime += 1; 98 | endTime -= 1; 99 | 100 | String bd = String.valueOf(DateUtils.getRelativeTimeSpanString(beginTime, nowTime, DateUtils.DAY_IN_MILLIS, DateUtils.FORMAT_SHOW_DATE)); 101 | String ed = String.valueOf(DateUtils.getRelativeTimeSpanString(endTime, nowTime, DateUtils.DAY_IN_MILLIS, DateUtils.FORMAT_SHOW_DATE)); 102 | 103 | int numberOfDays = Days.daysBetween(new DateTime(beginTime, DateTimeZone.getDefault()), new DateTime(endTime, DateTimeZone.getDefault())).getDays(); 104 | if (numberOfDays != 0) { 105 | return bd + eventFormatter.rangeSeparator + ed; 106 | } else { 107 | return bd; 108 | } 109 | } 110 | } 111 | 112 | static class EventFormatter { 113 | String timeSeparator; 114 | String rangeSeparator; 115 | String locationSeparator; 116 | boolean location; 117 | boolean locationWithTitle; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/helper/Constants.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.helper; 2 | 3 | import com.gobbledygook.theawless.eventlock.BuildConfig; 4 | 5 | import java.util.Collections; 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | public class Constants { 10 | 11 | // intent filters 12 | 13 | public static final String events_update = BuildConfig.APPLICATION_ID + "." + "EventsUpdate"; 14 | public static final String current_events_update = BuildConfig.APPLICATION_ID + "." + "CurrentEventUpdate"; 15 | public static final String looks_update = BuildConfig.APPLICATION_ID + "." + "LooksUpdate"; 16 | 17 | // intent extras 18 | 19 | public static final String formatted_titles = "formattedTitles"; 20 | public static final String formatted_times = "formattedTimes"; 21 | public static final String colors = "colors"; 22 | public static final String begin_times = "beginTimes"; 23 | public static final String end_times = "endTimes"; 24 | public static final String scroll_event = "scroll_event"; 25 | public static final String current_events = "currentEvents"; 26 | 27 | // keys 28 | 29 | public static final String selected_calendars_key = "selected_calendars"; 30 | public static final String days_till_key = "days_till"; 31 | 32 | public static final String text_position_key = "text_position"; 33 | 34 | public static final String time_alignment_key = "time_alignment"; 35 | public static final String time_font_size_key = "time_font_size"; 36 | public static final String time_padding_above_key = "time_padding_above"; 37 | public static final String time_padding_below_key = "time_padding_below"; 38 | public static final String time_padding_left_key = "time_padding_left"; 39 | public static final String time_padding_right_key = "time_padding_right"; 40 | 41 | public static final String title_alignment_key = "title_alignment"; 42 | public static final String title_font_size_key = "title_font_size"; 43 | public static final String title_padding_above_key = "title_padding_above"; 44 | public static final String title_padding_below_key = "title_padding_below"; 45 | public static final String title_padding_left_key = "title_padding_left"; 46 | public static final String title_padding_right_key = "title_padding_right"; 47 | 48 | public static final String number_of_events_key = "number_of_events"; 49 | public static final String gismo_scroll_direction_key = "gismo_scroll"; 50 | public static final String gismo_layout_direction_key = "gismo_layout"; 51 | public static final String gismo_padding_above_key = "gismo_padding_above"; 52 | public static final String gismo_padding_below_key = "gismo_padding_below"; 53 | public static final String gismo_padding_left_key = "gismo_padding_left"; 54 | public static final String gismo_padding_right_key = "gismo_padding_right"; 55 | 56 | public static final String color_position_key = "color_position"; 57 | public static final String show_color_key = "show_color"; 58 | public static final String color_stick_key = "color_stick"; 59 | public static final String color_type_key = "color_type"; 60 | public static final String color_anti_snap_height_key = "color_snap_height"; 61 | public static final String color_anti_snap_width_key = "color_snap_width"; 62 | public static final String color_height_key = "color_height"; 63 | public static final String color_width_key = "color_width"; 64 | public static final String color_padding_above_key = "color_padding_above"; 65 | public static final String color_padding_below_key = "color_padding_below"; 66 | public static final String color_padding_left_key = "color_padding_left"; 67 | public static final String color_padding_right_key = "color_padding_right"; 68 | 69 | public static final String current_title_bold_key = "current_title_bold"; 70 | public static final String current_time_bold_key = "current_time_bold"; 71 | public static final String current_color_outline_key = "current_color_outline"; 72 | 73 | public static final String time_separator_key = "time_separator"; 74 | public static final String range_separator_key = "range_separator"; 75 | public static final String location_separator_key = "location_separator"; 76 | public static final String location_key = "location"; 77 | public static final String dark_mode_key = "dark_mode"; 78 | 79 | // defaults 80 | public static final Set selected_calendars_default = new HashSet<>(Collections.singletonList("1")); 81 | 82 | public static final String days_till_default = "3"; 83 | 84 | public static final String text_position_default = "left"; 85 | 86 | public static final String time_font_size_default = "12"; 87 | public static final String time_alignment_default = "left"; 88 | public static final String time_padding_above_default = "0"; 89 | public static final String time_padding_below_default = "3"; 90 | public static final String time_padding_left_default = "0"; 91 | public static final String time_padding_right_default = "0"; 92 | 93 | public static final String title_font_size_default = "14"; 94 | public static final String title_alignment_default = "left"; 95 | public static final String title_padding_above_default = "0"; 96 | public static final String title_padding_below_default = "0"; 97 | public static final String title_padding_left_default = "0"; 98 | public static final String title_padding_right_default = "0"; 99 | 100 | public static final String number_of_events_default = "2"; 101 | public static final String gismo_scroll_direction_default = "vertical"; 102 | public static final String gismo_layout_direction_default = "vertical"; 103 | public static final String gismo_padding_above_default = "6"; 104 | public static final String gismo_padding_below_default = "0"; 105 | public static final String gismo_padding_left_default = "24"; 106 | public static final String gismo_padding_right_default = "24"; 107 | 108 | public static final String color_position_default = "left"; 109 | public static final String show_color_default = "true"; 110 | public static final String color_stick_default = "true"; 111 | public static final String color_type_default = "rectangle"; 112 | public static final String color_anti_snap_height_default = "false"; 113 | public static final String color_anti_width_default = "true"; 114 | public static final String color_height_default = "3"; 115 | public static final String color_width_default = "3"; 116 | public static final String color_padding_above_default = "0"; 117 | public static final String color_padding_below_default = "0"; 118 | public static final String color_padding_left_default = "0"; 119 | public static final String color_padding_right_default = "8"; 120 | 121 | public static final String current_title_bold_default = "true"; 122 | public static final String current_time_bold_default = "false"; 123 | public static final String current_color_outline_default = "true"; 124 | 125 | public static final String time_separator_default = ", "; 126 | public static final String range_separator_default = " - "; 127 | public static final String location_separator_default = " | "; 128 | public static final String location_default = "title"; 129 | public static final String dark_mode_default = "false"; 130 | 131 | private Constants() { 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/app/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.app; 2 | 3 | import android.app.AlertDialog; 4 | import android.content.DialogInterface; 5 | import android.content.Intent; 6 | import android.content.IntentFilter; 7 | import android.net.Uri; 8 | import android.os.Bundle; 9 | import android.preference.PreferenceManager; 10 | import android.support.v7.app.AppCompatActivity; 11 | import android.support.v7.widget.Toolbar; 12 | import android.view.Menu; 13 | import android.view.MenuItem; 14 | import android.view.View; 15 | import android.widget.GridLayout; 16 | import android.widget.Toast; 17 | 18 | import com.gobbledygook.theawless.eventlock.R; 19 | import com.gobbledygook.theawless.eventlock.gismo.EventsGismo; 20 | import com.gobbledygook.theawless.eventlock.helper.Constants; 21 | import com.gobbledygook.theawless.eventlock.helper.XposedUtils; 22 | import com.gobbledygook.theawless.eventlock.receivers.UpdateReceiver; 23 | import com.gobbledygook.theawless.eventlock.services.CalendarLoaderService; 24 | 25 | public class MainActivity extends AppCompatActivity { 26 | private boolean previewOn = false; 27 | private EventsGismo eventGismo = null; 28 | private final UpdateReceiver updateReceiver = new UpdateReceiver() { 29 | @Override 30 | protected EventsGismo getGismo(String action) { 31 | return eventGismo; 32 | } 33 | }; 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | if (savedInstanceState == null) { 39 | if (!XposedUtils.isModuleEnabled()) { 40 | showEnableModuleDialog(); 41 | return; 42 | } else if (XposedUtils.isModuleUpdated()) { 43 | showModuleUpdatedDialog(); 44 | return; 45 | } 46 | } 47 | onCreate(); 48 | } 49 | 50 | private void onCreate() { 51 | setContentView(R.layout.activity_main); 52 | setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); 53 | } 54 | 55 | @Override 56 | protected void onStart() { 57 | super.onStart(); 58 | IntentFilter intentFilter = new IntentFilter(); 59 | intentFilter.addAction(Constants.events_update); 60 | intentFilter.addAction(Constants.current_events_update); 61 | intentFilter.addAction(Constants.looks_update); 62 | registerReceiver(updateReceiver, intentFilter); 63 | } 64 | 65 | protected void onDestroy() { 66 | unregisterReceiver(updateReceiver); 67 | handleRefresh(); 68 | super.onDestroy(); 69 | } 70 | 71 | @Override 72 | public boolean onCreateOptionsMenu(Menu menu) { 73 | getMenuInflater().inflate(R.menu.actions, menu); 74 | return true; 75 | } 76 | 77 | @Override 78 | public boolean onOptionsItemSelected(MenuItem item) { 79 | switch (item.getItemId()) { 80 | case R.id.action_preview: { 81 | handlePreview(item); 82 | return true; 83 | } 84 | case R.id.action_refresh: { 85 | handleRefresh(); 86 | return true; 87 | } 88 | default: { 89 | return super.onOptionsItemSelected(item); 90 | } 91 | } 92 | } 93 | 94 | private void handlePreview(MenuItem item) { 95 | if (eventGismo == null) { 96 | GridLayout gridLayout = findViewById(R.id.events_placeholder); 97 | eventGismo = new EventsGismo(gridLayout, PreferenceManager.getDefaultSharedPreferences(this)); 98 | eventGismo.addRecyclerView(); 99 | handleRefresh(); 100 | } 101 | if (!previewOn) { 102 | findViewById(R.id.events_placeholder).setVisibility(View.VISIBLE); 103 | item.setIcon(getDrawable(R.drawable.action_preview_off_icon)); 104 | } else { 105 | findViewById(R.id.events_placeholder).setVisibility(View.GONE); 106 | item.setIcon(getDrawable(R.drawable.action_preview_on_icon)); 107 | } 108 | previewOn = !previewOn; 109 | } 110 | 111 | private void handleRefresh() { 112 | sendBroadcast(new Intent(Constants.looks_update)); 113 | startService(new Intent(this, CalendarLoaderService.class)); 114 | } 115 | 116 | private void startXposedActivity() { 117 | if (!XposedUtils.startXposedActivity(this)) { 118 | Toast.makeText(this, R.string.xposed_not_installed, Toast.LENGTH_SHORT).show(); 119 | startBrowserActivity(getString(R.string.xposed_forum_url)); 120 | } 121 | } 122 | 123 | void startBrowserActivity(String url) { 124 | Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); 125 | startActivity(intent); 126 | } 127 | 128 | private void showEnableModuleDialog() { 129 | new AlertDialog.Builder(this) 130 | .setTitle(R.string.enable_xposed_module_title) 131 | .setMessage(R.string.enable_xposed_module_message) 132 | .setIcon(R.drawable.warning_icon) 133 | .setCancelable(false) 134 | .setPositiveButton(R.string.enable, new DialogInterface.OnClickListener() { 135 | @Override 136 | public void onClick(DialogInterface dialog, int which) { 137 | startXposedActivity(); 138 | finish(); 139 | } 140 | }) 141 | .setNeutralButton(R.string.report_bug, new DialogInterface.OnClickListener() { 142 | @Override 143 | public void onClick(DialogInterface dialog, int which) { 144 | startBrowserActivity(getString(R.string.github_issues_url)); 145 | finish(); 146 | } 147 | }) 148 | .setNegativeButton(R.string.ignore, new DialogInterface.OnClickListener() { 149 | @Override 150 | public void onClick(DialogInterface dialog, int which) { 151 | onCreate(); 152 | } 153 | }) 154 | .show(); 155 | } 156 | 157 | private void showModuleUpdatedDialog() { 158 | new AlertDialog.Builder(this) 159 | .setTitle(R.string.module_outdated_title) 160 | .setMessage(R.string.module_outdated_message) 161 | .setIcon(R.drawable.warning_icon) 162 | .setCancelable(false) 163 | .setPositiveButton(R.string.clear_data, new DialogInterface.OnClickListener() { 164 | @Override 165 | public void onClick(DialogInterface dialog, int which) { 166 | startActivity(new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS) 167 | .addCategory(Intent.CATEGORY_DEFAULT) 168 | .setData(Uri.parse("package:" + getPackageName())) 169 | ); 170 | finish(); 171 | } 172 | }) 173 | .setNeutralButton(R.string.reboot, new DialogInterface.OnClickListener() { 174 | @Override 175 | public void onClick(DialogInterface dialog, int which) { 176 | startXposedActivity(); 177 | finish(); 178 | } 179 | }) 180 | .setNegativeButton(R.string.ignore, new DialogInterface.OnClickListener() { 181 | @Override 182 | public void onClick(DialogInterface dialog, int which) { 183 | onCreate(); 184 | } 185 | }) 186 | .show(); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/gismo/LayoutManagerHandler.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.gismo; 2 | 3 | import static com.gobbledygook.theawless.eventlock.helper.Utils.dpToPixel; 4 | 5 | import android.content.Context; 6 | import android.content.SharedPreferences; 7 | import android.graphics.Typeface; 8 | import android.support.v7.widget.GridLayoutManager; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.FrameLayout; 12 | import android.widget.GridLayout; 13 | import android.widget.TextView; 14 | 15 | import com.gobbledygook.theawless.eventlock.helper.Constants; 16 | import com.gobbledygook.theawless.eventlock.helper.Enums; 17 | 18 | class LayoutManagerHandler { 19 | private final SharedPreferences preferences; 20 | private final GridLayout gridLayout; 21 | private final int divisionFactors[] = new int[]{1 /* external use */, 1 /* internal use */}; 22 | private View eventView; 23 | private GridLayoutManager gridLayoutManager; 24 | private int dimensions[][]; 25 | 26 | LayoutManagerHandler(GridLayout gridLayout, SharedPreferences preferences) { 27 | this.preferences = preferences; 28 | this.gridLayout = gridLayout; 29 | setupDefaultDimensions(); 30 | } 31 | 32 | private void setupDefaultDimensions() { 33 | dimensions = new int[][]{ 34 | new int[]{1, 1}, // color 35 | new int[]{ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT}, // eventview 36 | new int[]{ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT}, // recyclerview 37 | }; 38 | } 39 | 40 | void construct(Runnable runnable) { 41 | setupDefaultDimensions(); 42 | Context gismoContext = gridLayout.getContext(); 43 | setupEventView(); 44 | addEventView(new int[]{ 45 | dpToPixel(gismoContext, preferences.getString(Constants.gismo_padding_left_key, Constants.gismo_padding_left_default)), 46 | dpToPixel(gismoContext, preferences.getString(Constants.gismo_padding_above_key, Constants.gismo_padding_above_default)), 47 | dpToPixel(gismoContext, preferences.getString(Constants.gismo_padding_right_key, Constants.gismo_padding_right_default)), 48 | dpToPixel(gismoContext, preferences.getString(Constants.gismo_padding_below_key, Constants.gismo_padding_below_default)) 49 | } 50 | ); 51 | // trying my best to getHeight lol 52 | gridLayout.post(runnable); 53 | } 54 | 55 | private void setupEventView() { 56 | Context gismoContext = gridLayout.getContext(); 57 | eventView = new EventViewBuildDirector(gismoContext, preferences) 58 | .setBuilder( 59 | new EventViewBuilder(gismoContext) { 60 | @Override 61 | protected void setupFullContainerRelativeLayout(int[] eventViewDimensions) { 62 | super.setupFullContainerRelativeLayout(eventViewDimensions); 63 | fullContainerRelativeLayout.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT)); 64 | } 65 | } 66 | ) 67 | .setInnerDimensions(getInnerDimensions()) 68 | .getEventView(); 69 | TextView titleView = eventView.findViewWithTag(Enums.ItemTag.Title); 70 | titleView.setTypeface(titleView.getTypeface(), Typeface.BOLD); 71 | TextView timeView = eventView.findViewWithTag(Enums.ItemTag.Time); 72 | timeView.setTypeface(timeView.getTypeface(), Typeface.BOLD); 73 | } 74 | 75 | private void addEventView(int[] gismoPadding) { 76 | FrameLayout frameLayout = new FrameLayout(gridLayout.getContext()); 77 | frameLayout.addView(eventView); 78 | gridLayout.addView(frameLayout); 79 | frameLayout.getLayoutParams().width = GridLayout.LayoutParams.MATCH_PARENT; 80 | frameLayout.getLayoutParams().height = GridLayout.LayoutParams.WRAP_CONTENT; 81 | frameLayout.setPadding(gismoPadding[0], gismoPadding[1], gismoPadding[2], gismoPadding[3]); 82 | } 83 | 84 | void doAfterPost() { 85 | findMeasurements(); 86 | gridLayout.removeView((FrameLayout) eventView.getParent()); 87 | } 88 | 89 | // man this is hard! had to try loads of combinations to get both width and height 90 | private void findMeasurements() { 91 | Context gismoContext = gridLayout.getContext(); 92 | ((FrameLayout) eventView.getParent()).measure(GridLayout.LayoutParams.MATCH_PARENT, GridLayout.LayoutParams.WRAP_CONTENT); 93 | int width = gridLayout.getWidth() 94 | - dpToPixel(gismoContext, preferences.getString(Constants.gismo_padding_left_key, Constants.gismo_padding_left_default)) 95 | - dpToPixel(gismoContext, preferences.getString(Constants.gismo_padding_right_key, Constants.gismo_padding_right_default)); 96 | dimensions[Enums.Dimensions.EventView.ordinal()][1] = dimensions[Enums.Dimensions.ColorImageView.ordinal()][1] = eventView.getMeasuredHeight(); 97 | if (getGridLayoutManager().getOrientation() == GridLayoutManager.VERTICAL) { 98 | dimensions[Enums.Dimensions.RecyclerView.ordinal()][1] = ((FrameLayout) eventView.getParent()).getMeasuredHeight() + (dimensions[Enums.Dimensions.EventView.ordinal()][1] * (divisionFactors[0] - 1)); 99 | dimensions[Enums.Dimensions.ColorImageView.ordinal()][0] = width / divisionFactors[1]; 100 | } else { 101 | dimensions[Enums.Dimensions.EventView.ordinal()][0] = dimensions[Enums.Dimensions.ColorImageView.ordinal()][0] = width / divisionFactors[0]; 102 | } 103 | } 104 | 105 | void decideLayoutManager() { 106 | /* 107 | ------------------------------------------------------------------------------------------------ 108 | Type Scroll Handler Color 109 | ------------------------------------------------------------------------------------------------ 110 | 1*n H V grid, vertical, n columns, limit height to one holder w/n 111 | 1*n H H grid, horizontal, 1 row, limit width of holders - divide by n w/n 112 | 113 | m*1 V V grid, vertical, 1 column, limit height to m holder w 114 | m*1 V H grid, horizontal, m rows w 115 | ----------------------------------------------------------------------------------------------- 116 | */ 117 | String orientation = preferences.getString(Constants.gismo_layout_direction_key, Constants.gismo_layout_direction_default); 118 | String scroll = preferences.getString(Constants.gismo_scroll_direction_key, Constants.gismo_scroll_direction_default); 119 | int division = Integer.parseInt(preferences.getString(Constants.number_of_events_key, Constants.number_of_events_default)); 120 | Context context = gridLayout.getContext(); 121 | String horizontal = "horizontal", vertical = "vertical"; 122 | if (orientation.equals(horizontal) && scroll.equals(vertical)) { 123 | divisionFactors[0] = 1; 124 | divisionFactors[1] = division; 125 | gridLayoutManager = new GridLayoutManager(context, division, GridLayoutManager.VERTICAL, false); 126 | } else if (orientation.equals(horizontal) && scroll.equals(horizontal)) { 127 | divisionFactors[0] = division; 128 | divisionFactors[1] = 1; 129 | gridLayoutManager = new GridLayoutManager(context, 1, GridLayoutManager.HORIZONTAL, false); 130 | } else if (orientation.equals(vertical) && scroll.equals(vertical)) { 131 | divisionFactors[0] = division; 132 | divisionFactors[1] = 1; 133 | gridLayoutManager = new GridLayoutManager(context, 1, GridLayoutManager.VERTICAL, false); 134 | } else if (orientation.equals(vertical) && scroll.equals(horizontal)) { 135 | divisionFactors[0] = 1; 136 | divisionFactors[1] = division; 137 | gridLayoutManager = new GridLayoutManager(context, division, GridLayoutManager.HORIZONTAL, false); 138 | } 139 | } 140 | 141 | GridLayoutManager getGridLayoutManager() { 142 | return gridLayoutManager; 143 | } 144 | 145 | int getDivisionFactor() { 146 | return divisionFactors[0]; 147 | } 148 | 149 | int[] getOuterDimensions() { 150 | return dimensions[Enums.Dimensions.RecyclerView.ordinal()]; 151 | } 152 | 153 | int[][] getInnerDimensions() { 154 | return new int[][]{dimensions[Enums.Dimensions.ColorImageView.ordinal()], dimensions[Enums.Dimensions.EventView.ordinal()]}; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/app/SettingsFragment.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.app; 2 | 3 | import android.Manifest; 4 | import android.annotation.SuppressLint; 5 | import android.app.AlertDialog; 6 | import android.content.Context; 7 | import android.content.DialogInterface; 8 | import android.content.pm.PackageManager; 9 | import android.os.Build; 10 | import android.os.Bundle; 11 | import android.preference.Preference; 12 | import android.preference.PreferenceFragment; 13 | import android.preference.PreferenceScreen; 14 | import android.support.annotation.NonNull; 15 | import android.widget.Toast; 16 | 17 | import com.gobbledygook.theawless.eventlock.R; 18 | import com.gobbledygook.theawless.eventlock.helper.Constants; 19 | 20 | public class SettingsFragment extends PreferenceFragment { 21 | private static final int CALENDAR_READ_REQUEST_CODE = 0; 22 | private static final int nonEmptyPreferences[] = new int[]{ 23 | R.string.gismo_padding_above_key, R.string.gismo_padding_below_key, R.string.gismo_padding_left_key, R.string.gismo_padding_right_key, 24 | R.string.title_padding_above_key, R.string.title_padding_below_key, R.string.title_padding_left_key, R.string.title_padding_right_key, 25 | R.string.time_padding_above_key, R.string.time_padding_below_key, R.string.time_padding_left_key, R.string.time_padding_right_key, 26 | R.string.color_padding_above_key, R.string.color_padding_below_key, R.string.color_padding_left_key, R.string.color_padding_right_key, 27 | R.string.color_height_key, R.string.color_width_key, R.string.days_till_key, 28 | R.string.number_of_events_key, R.string.title_font_size_key, R.string.time_font_size_key, 29 | }; 30 | 31 | private final Preference.OnPreferenceChangeListener preferenceListener = new Preference.OnPreferenceChangeListener() { 32 | @Override 33 | public boolean onPreferenceChange(Preference preference, Object newValue) { 34 | return !newValue.toString().trim().isEmpty(); 35 | } 36 | }; 37 | private int versionClickCount = 0; 38 | private PresetMaker presetMaker; 39 | 40 | @SuppressLint("WorldReadableFiles") 41 | @Override 42 | public void onCreate(Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | getPreferenceManager().setSharedPreferencesMode(Context.MODE_WORLD_READABLE); 45 | addPreferencesFromResource(R.xml.preferences); 46 | presetMaker = new PresetMaker(getActivity()); 47 | } 48 | 49 | @Override 50 | public void onStart() { 51 | super.onStart(); 52 | setUpPreferenceCleaners(); 53 | permissionCheck(); 54 | } 55 | 56 | private void permissionCheck() { 57 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && getActivity().checkSelfPermission(Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED) { 58 | requestPermissions(new String[]{Manifest.permission.READ_CALENDAR}, CALENDAR_READ_REQUEST_CODE); 59 | } else { 60 | createCalendarList(); 61 | } 62 | } 63 | 64 | private void refreshUI() { 65 | setPreferenceScreen(null); 66 | addPreferencesFromResource(R.xml.preferences); 67 | permissionCheck(); 68 | setUpPreferenceCleaners(); 69 | } 70 | 71 | @Override 72 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 73 | if (requestCode == CALENDAR_READ_REQUEST_CODE) { 74 | if (grantResults.length <= 0 || grantResults[0] != PackageManager.PERMISSION_GRANTED) { 75 | Toast.makeText(getActivity(), R.string.read_permission_error, Toast.LENGTH_SHORT).show(); 76 | getActivity().finish(); 77 | } else { 78 | createCalendarList(); 79 | } 80 | } 81 | } 82 | 83 | private void createCalendarList() { 84 | ((CalendarPreference) findPreference(Constants.selected_calendars_key)).getCalendarIds(); 85 | } 86 | 87 | private void setUpPreferenceCleaners() { 88 | for (int keyId : nonEmptyPreferences) { 89 | Preference preference = getPreferenceManager().findPreference(getString(keyId)); 90 | if (preference != null) { 91 | preference.setOnPreferenceChangeListener(preferenceListener); 92 | } 93 | } 94 | } 95 | 96 | @Override 97 | public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 98 | switch (preference.getTitleRes()) { 99 | case R.string.version_title: { 100 | String resourceName = "version_count_" + String.valueOf(++versionClickCount); 101 | int id = getResources().getIdentifier(resourceName, "string", getActivity().getPackageName()); 102 | if (id != 0) { 103 | Toast.makeText(getActivity(), getResources().getString(id), Toast.LENGTH_SHORT).show(); 104 | } 105 | return true; 106 | } 107 | case R.string.project_title: { 108 | new AlertDialog.Builder(getActivity()) 109 | .setTitle(R.string.project_title) 110 | .setMessage(R.string.project) 111 | .setIcon(R.drawable.action_preview_on_icon) 112 | .setNeutralButton(R.string.report_bug, new DialogInterface.OnClickListener() { 113 | @Override 114 | public void onClick(DialogInterface dialog, int which) { 115 | ((MainActivity) getActivity()).startBrowserActivity(getString(R.string.github_issues_url)); 116 | } 117 | }) 118 | .setNegativeButton(R.string.paypal, new DialogInterface.OnClickListener() { 119 | @Override 120 | public void onClick(DialogInterface dialog, int which) { 121 | ((MainActivity) getActivity()).startBrowserActivity(getString(R.string.paypal_url)); 122 | } 123 | }) 124 | .setPositiveButton(R.string.github, new DialogInterface.OnClickListener() { 125 | @Override 126 | public void onClick(DialogInterface dialog, int which) { 127 | ((MainActivity) getActivity()).startBrowserActivity(getString(R.string.github_url)); 128 | } 129 | }) 130 | .show(); 131 | return true; 132 | } 133 | case R.string.help_title: { 134 | new AlertDialog.Builder(getActivity()) 135 | .setTitle(R.string.help_title) 136 | .setMessage(R.string.help) 137 | .setIcon(R.drawable.action_refresh_icon) 138 | .setPositiveButton(R.string.okay, null) 139 | .show(); 140 | return true; 141 | } 142 | case R.string.multiple_preset1_title: { 143 | presetMaker.begin() 144 | .lineLeft() 145 | .makeTextSpaceVertically() 146 | .setOrientations("vertical", "vertical") 147 | .end(); 148 | break; 149 | } 150 | case R.string.multiple_preset2_title: { 151 | presetMaker.begin() 152 | .circleBelow() 153 | .makeColorSpaceVertically() 154 | .centerText() 155 | .end(); 156 | break; 157 | } 158 | case R.string.multiple_preset3_title: { 159 | presetMaker.begin() 160 | .lineLeft() 161 | .end(); 162 | break; 163 | } 164 | case R.string.multiple_preset4_title: { 165 | presetMaker.begin() 166 | .circleRight() 167 | .makeTextSpaceVertically() 168 | .makeColorSpaceVertically() 169 | .setOrientations("vertical", "vertical") 170 | .end(); 171 | break; 172 | } 173 | case R.string.multiple_preset5_title: { 174 | presetMaker.begin() 175 | .lineAbove() 176 | .end(); 177 | break; 178 | } 179 | case R.string.multiple_preset6_title: { 180 | presetMaker.begin() 181 | .eclipseAbove() 182 | .setOrientations("vertical", "vertical") 183 | .centerText() 184 | .makeTextSpaceVertically() 185 | .end(); 186 | break; 187 | } 188 | case R.string.single_preset1_title: { 189 | presetMaker.begin() 190 | .circleRight() 191 | .setMultipleEvents(1) 192 | .makeTextBig() 193 | .end(); 194 | break; 195 | } 196 | case R.string.single_preset2_title: { 197 | presetMaker.begin() 198 | .setMultipleEvents(1) 199 | .makeTextBig() 200 | .end(); 201 | break; 202 | } 203 | case R.string.single_preset3_title: { 204 | presetMaker.begin() 205 | .noColor() 206 | .setMultipleEvents(1) 207 | .makeTextBig() 208 | .end(); 209 | break; 210 | } 211 | default: { 212 | return super.onPreferenceTreeClick(preferenceScreen, preference); 213 | } 214 | } 215 | // ends up here if one of the presets is clicked, otherwise returns early 216 | refreshUI(); 217 | return true; 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Xposed framework is not installed 4 | Enable this module 5 | For EventLock to function correctly, you must enable the Xposed Module 6 | Enable 7 | Okay 8 | Report bug 9 | Clear app data 10 | Ignore 11 | Xposed module updated 12 | Due to various new changes you must clear the app-data once. 13 | Reboot 14 | Calendar read permission is required. Exiting! 15 | Settings set to preset, press refresh! 16 | Please press the refresh button after making any setting changes.\n\nThe eye button in the title bar can be used to show/hide preview.\n\nKeeping the bottom padding in Gismo high will lead to more space between notifications and events.\n\nTo make the oval color block circular, set height equal to width.\n\nThere are loads of combinations possible! Mess up those settings! :-D 17 | Hey,\n\nI built this project for my personal use, but I am glad that it could help you too.\n\nMany people have helped me (on Github and XDA) in testing and by suggesting new ideas. Special mention to Mansya Prime for designing the icon.\n\nThanks a lot everyone! 18 | Github 19 | Donate 20 | 21 | 22 | Preview 23 | Refresh 24 | 25 | 26 | Settings 27 | Customisation 28 | Presets 29 | Multiple 30 | Single 31 | Padding 32 | Gismo 33 | Gismo Padding 34 | Current event 35 | Title & Time 36 | Container 37 | Title 38 | Title padding 39 | Time 40 | Time padding 41 | Color 42 | Color padding 43 | About 44 | Help 45 | Details about various features 46 | Project 47 | Credits and urls 48 | 49 | Preset 1 50 | Preset 2 51 | Preset 3 52 | Preset 4 53 | Preset 5 54 | Preset 6 55 | 56 | Preset 1 57 | Preset 2 58 | Preset 3 59 | 60 | These calendar will be used to show the events 61 | Selected calendars 62 | How many days after today should we look for events 63 | Days till 64 | 65 | Where should the text be placed on the lockscreen 66 | Text position 67 | 68 | The display size of time 69 | Font size 70 | The display alignment of time 71 | Alignment 72 | Time\'s padding from above in the lock screen 73 | Above 74 | Time\'s padding from below in the lock screen 75 | Below 76 | Time\'s padding from right in the lock screen 77 | Right 78 | Time\'s padding from left in the lock screen 79 | Left 80 | 81 | The display size of title 82 | Font size 83 | The display alignment of title 84 | Alignment 85 | Title\'s padding from above in the lock screen 86 | Above 87 | Title\'s padding from below in the lock screen 88 | Below 89 | Title\'s padding from right in the lock screen 90 | Right 91 | Title\'s padding from left in the lock screen 92 | Left 93 | 94 | Number of events on the same page 95 | Number of events 96 | 97 | Gismo\'s scrolling direction 98 | Scroll direction 99 | Gismo\'s layout direction 100 | Layout direction 101 | 102 | Gismo\'s padding from above in the lock screen 103 | Above 104 | Gismo\'s padding from below in the lock screen 105 | Below 106 | Gismo\'s padding from right in the lock screen 107 | Right 108 | Gismo\'s padding from left in the lock screen 109 | Left 110 | 111 | How color block should be placed 112 | Color position 113 | Show event color on the lock screen 114 | Show event color 115 | Whether the color block should be placed very close to text 116 | Stick to text 117 | Block type 118 | Color block type on the lockscreen 119 | Disable setting color height based on text size 120 | Manual Height 121 | Disable setting color width based on text size 122 | Manual width 123 | The height of the color block 124 | Height 125 | The width of the color block 126 | Width 127 | Color\'s padding from above in the lock screen 128 | Above 129 | Color\'s padding from below in the lock screen 130 | Below 131 | Color\'s padding from right in the lock screen 132 | Right 133 | Color\'s padding from left in the lock screen 134 | Left 135 | 136 | Bold the title of the current event 137 | Title bold 138 | Bold the time of the current event 139 | Time bold 140 | Outline the color block of the current event 141 | Color outline 142 | 143 | Time separator 144 | Separates date and time 145 | Range separator 146 | Separates time range 147 | Location separator 148 | Separates location from title or time 149 | Location 150 | Where should location be shown 151 | Dark mode 152 | Helpful when the background is bright 153 | 154 | 155 | 156 | title 157 | time 158 | off 159 | 160 | 161 | left 162 | right 163 | up 164 | down 165 | 166 | 167 | left 168 | center 169 | right 170 | 171 | 172 | oval 173 | rectangle 174 | 175 | 176 | horizontal 177 | vertical 178 | 179 | 180 | -------------------------------------------------------------------------------- /app/src/main/java/com/gobbledygook/theawless/eventlock/gismo/EventViewBuilder.java: -------------------------------------------------------------------------------- 1 | package com.gobbledygook.theawless.eventlock.gismo; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.graphics.drawable.Drawable; 6 | import android.graphics.drawable.GradientDrawable; 7 | import android.graphics.drawable.LayerDrawable; 8 | import android.graphics.drawable.ShapeDrawable; 9 | import android.graphics.drawable.shapes.OvalShape; 10 | import android.graphics.drawable.shapes.RectShape; 11 | import android.support.v7.widget.GridLayoutManager; 12 | import android.text.TextUtils; 13 | import android.view.View; 14 | import android.widget.ImageView; 15 | import android.widget.LinearLayout; 16 | import android.widget.RelativeLayout; 17 | import android.widget.TextView; 18 | 19 | import com.gobbledygook.theawless.eventlock.helper.Enums; 20 | 21 | class EventViewBuilder { 22 | private final Context gismoContext; 23 | RelativeLayout fullContainerRelativeLayout; 24 | private LinearLayout textContainerLinearLayout; 25 | private ImageView colorImageView; 26 | 27 | EventViewBuilder(Context gismoContext) { 28 | this.gismoContext = gismoContext; 29 | } 30 | 31 | void setupFullContainerRelativeLayout(int[] eventViewDimensions) { 32 | fullContainerRelativeLayout = new RelativeLayout(gismoContext); 33 | fullContainerRelativeLayout.setLayoutParams(new GridLayoutManager.LayoutParams(eventViewDimensions[0], eventViewDimensions[1])); 34 | } 35 | 36 | void setupTextContainerLinearLayout() { 37 | textContainerLinearLayout = new LinearLayout(gismoContext); 38 | textContainerLinearLayout.setOrientation(LinearLayout.VERTICAL); 39 | fullContainerRelativeLayout.addView(textContainerLinearLayout); 40 | textContainerLinearLayout.getLayoutParams().height = RelativeLayout.LayoutParams.WRAP_CONTENT; 41 | textContainerLinearLayout.getLayoutParams().width = RelativeLayout.LayoutParams.WRAP_CONTENT; 42 | } 43 | 44 | void setupTitleTextView(int[] padding, int size, String alignment, boolean dark_mode) { 45 | TextView titleTextView = new TextView(gismoContext); 46 | textContainerLinearLayout.addView(titleTextView); 47 | setupCommonTextView(titleTextView, padding, size, alignment); 48 | titleTextView.setTag(Enums.ItemTag.Title); 49 | titleTextView.setTextColor(dark_mode ? Color.BLACK : Color.WHITE); 50 | } 51 | 52 | void setupTimeTextView(int[] padding, int size, String alignment, boolean dark_mode) { 53 | TextView timeTextView = new TextView(gismoContext); 54 | textContainerLinearLayout.addView(timeTextView); 55 | setupCommonTextView(timeTextView, padding, size, alignment); 56 | timeTextView.setTag(Enums.ItemTag.Time); 57 | timeTextView.setTextColor(dark_mode ? Color.BLACK : Color.WHITE); 58 | } 59 | 60 | private void setupCommonTextView(TextView textView, int[] padding, int size, String alignment) { 61 | textView.setMaxLines(1); 62 | textView.setHorizontallyScrolling(true); 63 | textView.setEllipsize(TextUtils.TruncateAt.MARQUEE); 64 | textView.setMarqueeRepeatLimit(-1); 65 | textView.setSelected(true); 66 | textView.setTextSize(size); 67 | textView.setPadding(padding[0], padding[1], padding[2], padding[3]); 68 | textView.getLayoutParams().height = LinearLayout.LayoutParams.WRAP_CONTENT; 69 | textView.getLayoutParams().width = LinearLayout.LayoutParams.MATCH_PARENT; 70 | switch (alignment) { 71 | case "left": { 72 | textView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START); 73 | break; 74 | } 75 | case "right": { 76 | textView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_END); 77 | break; 78 | } 79 | case "center": { 80 | textView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); 81 | } 82 | } 83 | } 84 | 85 | void setupColorImageView(int[] padding, String type, int[] manualDimensions, boolean[] isManualDimensions, int[] autoDimensions) { 86 | colorImageView = new ImageView(gismoContext); 87 | fullContainerRelativeLayout.addView(colorImageView); 88 | colorImageView.setTag(Enums.ItemTag.Image); 89 | GradientDrawable outlineDrawable = new GradientDrawable(); 90 | ShapeDrawable shapeDrawable = new ShapeDrawable(); 91 | if (type.equals("oval")) { 92 | shapeDrawable.setShape(new OvalShape()); 93 | outlineDrawable.setShape(GradientDrawable.OVAL); 94 | } else { 95 | shapeDrawable.setShape(new RectShape()); 96 | outlineDrawable.setShape(GradientDrawable.RECTANGLE); 97 | } 98 | shapeDrawable.setIntrinsicWidth(isManualDimensions[0] ? manualDimensions[0] : autoDimensions[0]); 99 | shapeDrawable.setIntrinsicHeight(isManualDimensions[1] ? manualDimensions[1] : autoDimensions[1]); 100 | colorImageView.setImageDrawable(new LayerDrawable(new Drawable[]{shapeDrawable, outlineDrawable})); 101 | colorImageView.getLayoutParams().height = RelativeLayout.LayoutParams.WRAP_CONTENT; 102 | colorImageView.getLayoutParams().width = RelativeLayout.LayoutParams.WRAP_CONTENT; 103 | colorImageView.setPadding(padding[0], padding[1], padding[2], padding[3]); 104 | colorImageView.setAdjustViewBounds(true); 105 | } 106 | 107 | /* 108 | ----------------------------------------------------------------------------------------------------------- 109 | Text Position Color Position Stick Color Status(T for text, C for color, OO for overlap) 110 | ----------------------------------------------------------------------------------------------------------- 111 | 112 | left left true | | 113 | |CT | 114 | | | 115 | 116 | false | | 117 | |OO | 118 | | | 119 | 120 | right true | | 121 | |TC | 122 | | | 123 | 124 | false | | 125 | |T C| 126 | | | 127 | 128 | up true |C | 129 | |T | 130 | | | 131 | 132 | false | C | 133 | |T | 134 | | | 135 | 136 | down true | | 137 | |T | 138 | |C | 139 | 140 | false | | 141 | |T | 142 | | C | 143 | 144 | right left true | | 145 | | CT| 146 | | | 147 | 148 | false | | 149 | |C T| 150 | | | 151 | 152 | right true | | 153 | | TC| 154 | | | 155 | 156 | false | | 157 | | OO| 158 | | | 159 | 160 | up true | C| 161 | | T| 162 | | | 163 | 164 | false | C | 165 | | T| 166 | | | 167 | 168 | down true | | 169 | | T| 170 | | C| 171 | 172 | false | | 173 | | T| 174 | | C | 175 | 176 | center left true | | 177 | | CT | 178 | | | 179 | 180 | false | | 181 | |C T | 182 | | | 183 | 184 | right true | | 185 | | TC | 186 | | | 187 | 188 | false | | 189 | | T C| 190 | | | 191 | 192 | up true | C | 193 | | T | 194 | | | 195 | 196 | false | C | 197 | | T | 198 | | | 199 | 200 | down true | | 201 | | T | 202 | | C | 203 | 204 | false | | 205 | | T | 206 | | C | 207 | 208 | ----------------------------------------------------------------------------------------------------------- 209 | */ 210 | 211 | 212 | // never look at this code. not even god knows what I wrote 213 | void setupPositions(String textPosition, String colorPosition, boolean stickColor) { 214 | int textPositionRule; 215 | switch (textPosition) { 216 | case "left": { 217 | textPositionRule = RelativeLayout.ALIGN_PARENT_LEFT; 218 | break; 219 | } 220 | case "center": { 221 | textPositionRule = RelativeLayout.CENTER_HORIZONTAL; 222 | break; 223 | } 224 | case "right": 225 | default: { 226 | textPositionRule = RelativeLayout.ALIGN_PARENT_RIGHT; 227 | } 228 | } 229 | ((RelativeLayout.LayoutParams) textContainerLinearLayout.getLayoutParams()).addRule(textPositionRule); 230 | ((RelativeLayout.LayoutParams) textContainerLinearLayout.getLayoutParams()).addRule(RelativeLayout.CENTER_VERTICAL); 231 | if (colorImageView == null) { 232 | return; 233 | } 234 | textContainerLinearLayout.setId(View.generateViewId()); 235 | colorImageView.setId(View.generateViewId()); 236 | switch (colorPosition) { 237 | case "left": { 238 | if (!stickColor) { 239 | ((RelativeLayout.LayoutParams) colorImageView.getLayoutParams()).addRule(RelativeLayout.ALIGN_PARENT_LEFT); 240 | ((RelativeLayout.LayoutParams) colorImageView.getLayoutParams()).addRule(RelativeLayout.CENTER_VERTICAL); 241 | break; 242 | } 243 | if (textPosition.equals("left")) { 244 | ((RelativeLayout.LayoutParams) colorImageView.getLayoutParams()).addRule(RelativeLayout.ALIGN_PARENT_LEFT); 245 | ((RelativeLayout.LayoutParams) textContainerLinearLayout.getLayoutParams()).removeRule(RelativeLayout.ALIGN_PARENT_LEFT); 246 | ((RelativeLayout.LayoutParams) textContainerLinearLayout.getLayoutParams()).addRule(RelativeLayout.RIGHT_OF, colorImageView.getId()); 247 | } else { 248 | ((RelativeLayout.LayoutParams) colorImageView.getLayoutParams()).addRule(RelativeLayout.LEFT_OF, textContainerLinearLayout.getId()); 249 | } 250 | ((RelativeLayout.LayoutParams) colorImageView.getLayoutParams()).addRule(RelativeLayout.CENTER_VERTICAL); 251 | break; 252 | } 253 | case "right": { 254 | if (!stickColor) { 255 | ((RelativeLayout.LayoutParams) colorImageView.getLayoutParams()).addRule(RelativeLayout.ALIGN_PARENT_RIGHT); 256 | ((RelativeLayout.LayoutParams) colorImageView.getLayoutParams()).addRule(RelativeLayout.CENTER_VERTICAL); 257 | break; 258 | } 259 | if (textPosition.equals("right")) { 260 | ((RelativeLayout.LayoutParams) colorImageView.getLayoutParams()).addRule(RelativeLayout.ALIGN_PARENT_RIGHT); 261 | ((RelativeLayout.LayoutParams) textContainerLinearLayout.getLayoutParams()).removeRule(RelativeLayout.ALIGN_PARENT_RIGHT); 262 | ((RelativeLayout.LayoutParams) textContainerLinearLayout.getLayoutParams()).addRule(RelativeLayout.LEFT_OF, colorImageView.getId()); 263 | } else { 264 | ((RelativeLayout.LayoutParams) colorImageView.getLayoutParams()).addRule(RelativeLayout.RIGHT_OF, textContainerLinearLayout.getId()); 265 | } 266 | ((RelativeLayout.LayoutParams) colorImageView.getLayoutParams()).addRule(RelativeLayout.CENTER_VERTICAL); 267 | break; 268 | } 269 | case "up": { 270 | if (stickColor) { 271 | ((RelativeLayout.LayoutParams) colorImageView.getLayoutParams()).addRule(textPositionRule); 272 | } 273 | ((RelativeLayout.LayoutParams) colorImageView.getLayoutParams()).addRule(RelativeLayout.ALIGN_PARENT_TOP); 274 | ((RelativeLayout.LayoutParams) textContainerLinearLayout.getLayoutParams()).addRule(RelativeLayout.BELOW, colorImageView.getId()); 275 | break; 276 | } 277 | case "down": { 278 | if (stickColor) { 279 | ((RelativeLayout.LayoutParams) colorImageView.getLayoutParams()).addRule(textPositionRule); 280 | } 281 | ((RelativeLayout.LayoutParams) textContainerLinearLayout.getLayoutParams()).removeRule(RelativeLayout.CENTER_VERTICAL); 282 | ((RelativeLayout.LayoutParams) textContainerLinearLayout.getLayoutParams()).addRule(RelativeLayout.ALIGN_PARENT_TOP); 283 | ((RelativeLayout.LayoutParams) colorImageView.getLayoutParams()).addRule(RelativeLayout.BELOW, textContainerLinearLayout.getId()); 284 | break; 285 | } 286 | } 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /app/src/main/res/xml/preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 17 | 18 | 19 | 20 | 21 | 24 | 27 | 30 | 33 | 36 | 39 | 40 | 41 | 44 | 47 | 50 | 51 | 52 | 53 | 54 | 61 | 68 | 75 | 76 | 77 | 84 | 91 | 98 | 105 | 106 | 107 | 108 | 109 | 114 | 119 | 124 | 129 | 130 | 131 | 132 | 133 | 140 | 141 | 142 | 149 | 156 | 157 | 158 | 165 | 172 | 179 | 186 | 187 | 188 | 189 | 190 | 197 | 204 | 209 | 214 | 215 | 216 | 223 | 230 | 237 | 244 | 245 | 246 | 247 | 248 | 255 | 260 | 261 | 262 | 263 | 264 | 269 | 277 | 283 | 291 | 297 | 303 | 311 | 319 | 322 | 323 | 331 | 339 | 347 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 364 | 367 | 368 | 369 | 370 | --------------------------------------------------------------------------------