Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 461e4862 authored by LuK1337's avatar LuK1337
Browse files

Merge tag 'v1.0.53' of https://github.com/Etar-Group/Etar-Calendar into lineage-23.2

* tag 'v1.0.53' of https://github.com/Etar-Group/Etar-Calendar:
  New version 1.0.53
  Fix duplicate contact birthday notifications
  Bump actions/upload-artifact from 6 to 7
  Update dependencies
  Translated using Weblate (Italian)
  Translated using Weblate (Korean)
  Translated using Weblate (Italian)
  Translated using Weblate (Czech)
  Translated using Weblate (English (United Kingdom))
  Translated using Weblate (English (United Kingdom))
  Translated using Weblate (Dutch)
  Translated using Weblate (Chinese (Simplified Han script))
  Translated using Weblate (Korean)
  Translated using Weblate (Kurdish (Central))
  Translated using Weblate (Kabyle)
  Translated using Weblate (Kabyle)
  Translated using Weblate (Kurdish (Central))
  Added translation using Weblate (Kabyle)
  Translated using Weblate (Ukrainian)
  Translated using Weblate (Italian)
  Translated using Weblate (Welsh)
  Translated using Weblate (Welsh)
  Translated using Weblate (German)
  Translated using Weblate (Portuguese (Brazil))
  Translated using Weblate (Latvian)
  Translated using Weblate (Dutch)
  Translated using Weblate (Czech)
  Add dynamic calendar icon support for compatible launchers
  Use WorkManager instead of a Service
  Time switchTimezone: Also update timezone attribute
  Change dark color for better contrast
  Fix "black theme colors not loaded"
  Update workflow to java 25
  Update gradle(w) to 9.2.1
  Migrate AboutFragment from BuildConfig to Utils.getVersionCode()

Change-Id: I449258747a9396afe301cb5b7ab1a91486dc8daf
parents 7eebc641 21bf1362
Loading
Loading
Loading
Loading
+9 −13
Original line number Diff line number Diff line
import com.android.build.gradle.internal.tasks.factory.dependsOn
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.lineageos.generatebp.GenerateBpPluginExtension
import org.lineageos.generatebp.models.Module

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
    alias(libs.plugins.ec4j.editorconfig)
    alias(libs.plugins.lineageos.generatebp)
}

editorconfig {
	excludes = listOf("metadata/**", "**/*.webp")
	excludes = listOf("metadata/**", "**/*.xml", "**/*.webp")
}

kotlin {
@@ -25,9 +23,9 @@ android {

	defaultConfig {
		minSdk = 23
		targetSdk = 35
		versionCode = 52
		versionName = "1.0.52"
		targetSdk = 36
		versionCode = 53
		versionName = "1.0.53"
		applicationId = "ws.xsoh.etar"
		testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
	}
@@ -58,6 +56,7 @@ android {
	buildFeatures {
        buildConfig = true
		viewBinding = true
		resValues = true
	}

	/*
@@ -108,12 +107,6 @@ android {
		targetCompatibility(JavaVersion.VERSION_21)
	}

kotlin {
    compilerOptions {
         jvmTarget = JvmTarget.JVM_21
    }
}

	useLibrary("android.test.base")
	useLibrary("android.test.mock")

@@ -131,7 +124,10 @@ dependencies {
	implementation(libs.androidx.appcompat)
	implementation(libs.androidx.constraintlayout)
	implementation(libs.google.android.material)
    implementation(libs.androidx.work.runtime)
    implementation(libs.androidx.concurrent.futures)
    testImplementation(libs.junit)
	testImplementation(libs.androidx.test.runner)

	coreLibraryDesugaring(libs.android.tools.desugar)

+7 −2
Original line number Diff line number Diff line
@@ -89,6 +89,9 @@
            <meta-data android:name="${applicationId}.dynamic_icons"
                android:resource="@array/calendar_icons_dynamic" />

            <meta-data android:name="com.teslacoilsw.launcher.calendarIconArray"
                android:resource="@array/calendar_icons_dynamic" />

        </activity>

        <activity-alias android:name="com.android.calendar.LaunchActivity"
@@ -271,9 +274,11 @@
            </intent-filter>
        </receiver>

        <service android:name="com.android.calendar.alerts.AlertService"
        <service
            android:name="androidx.work.impl.foreground.SystemForegroundService"
            android:foregroundServiceType= "systemExempted"
            android:exported="false"
            android:foregroundServiceType="systemExempted" />
            tools:node="merge" />

        <service android:name="com.android.calendar.alerts.DismissAlarmsService" />

+3 −57
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.calendar.alerts;

import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.app.TaskStackBuilder;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
@@ -118,51 +117,6 @@ public class AlertReceiver extends BroadcastReceiver {
        sAsyncHandler = new Handler(thr.getLooper());
    }

    /**
     * Start the service to process the current event notifications, acquiring
     * the wake lock before returning to ensure that the service will run.
     */
    public static void beginStartingService(Context context, Intent intent) {
        synchronized (mStartingServiceSync) {
            PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);

            if (mStartingService == null) {
                mStartingService = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                        "Etar:StartingAlertService");
                mStartingService.setReferenceCounted(false);
            }
            mStartingService.acquire();

            if (pm.isIgnoringBatteryOptimizations(context.getPackageName())) {
                if (Utils.isOreoOrLater()) {
                    if (Utils.isUpsideDownCakeOrLater() && !Utils.canScheduleAlarms(context)) {
                        return;
                    }
                    context.startForegroundService(intent);
                } else {
                    context.startService(intent);
                }
            } else {
                Log.d(TAG, "Battery optimizations are not disabled");
            }

        }
    }

    /**
     * Called back by the service when it has finished processing notifications,
     * releasing the wake lock if the service is now stopping.
     */
    public static void finishStartingService(Service service, int startId) {
        synchronized (mStartingServiceSync) {
            if (mStartingService != null) {
                if (service.stopSelfResult(startId)) {
                    mStartingService.release();
                }
            }
        }
    }

    private static PendingIntent createClickEventIntent(Context context, long eventId,
        int notificationId, long startMillis, long endMillis) {
        Intent intent = AlertUtils.buildEventViewIntent(context, eventId, startMillis, endMillis);
@@ -788,6 +742,8 @@ public class AlertReceiver extends BroadcastReceiver {
        if (context == null || intent.getAction() == null)
            return;

        String action = intent.getAction();

        if (AlertService.DEBUG) {
            Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent);
        }
@@ -847,17 +803,7 @@ public class AlertReceiver extends BroadcastReceiver {
                context.startActivity(i);
            }
        } else {
            Intent i = new Intent();
            i.setClass(context, AlertService.class);
            i.putExtras(intent);
            i.putExtra("action", intent.getAction());
            Uri uri = intent.getData();


            if (uri != null) {
                i.putExtra("uri", uri.toString());
            }
            beginStartingService(context, i);
            AlertUtils.scheduleAlertWorker(context, action);
        }
    }

+45 −110
Original line number Diff line number Diff line
@@ -16,14 +16,11 @@

package com.android.calendar.alerts;

import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED;

import android.Manifest;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -34,13 +31,6 @@ import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.provider.CalendarContract;
import android.provider.CalendarContract.Attendees;
import android.provider.CalendarContract.CalendarAlerts;
@@ -50,7 +40,6 @@ import android.util.Log;

import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.app.ServiceCompat;
import androidx.core.content.ContextCompat;

import com.android.calendar.Utils;
@@ -65,9 +54,9 @@ import java.util.TimeZone;
import ws.xsoh.etar.R;

/**
 * This service is used to handle calendar event reminders.
 * Static service class is used to handle calendar event reminders. Called by AlertWorker.
 */
public class AlertService extends Service {
public class AlertService {

    public static final String ALERT_CHANNEL_GROUP_ID = "alert_channel_group_01";
    public static final String FOREGROUND_CHANNEL_ID = "foreground_channel_01";
@@ -138,8 +127,6 @@ public class AlertService extends Service {
                    + " AND "
                    + CalendarContract.CalendarAlerts.END + ">=?";
    private static Boolean sReceivedProviderReminderBroadcast = null;
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;

    static void dismissOldAlerts(Context context) {
        ContentResolver cr = context.getContentResolver();
@@ -548,12 +535,25 @@ public class AlertService extends Service {
                if (sendAlert) {
                    if (state == CalendarAlerts.STATE_SCHEDULED || newAlertOverride) {
                        newState = CalendarAlerts.STATE_FIRED;

                        // When using local storage to track alert state (BYPASS_DB),
                        // check if this alert was already fired previously.  The
                        // calendar provider may re-insert SCHEDULED alert entries
                        // when events are re-synced (e.g. contact birthday events
                        // during a contact sync), which would otherwise cause
                        // duplicate notifications with sound/vibrate.
                        boolean alreadyFired = AlertUtils.BYPASS_DB
                                && AlertUtils.hasAlertFiredInSharedPrefs(
                                        context, eventId, beginTime, alarmTime);

                        if (!alreadyFired) {
                            numFired++;
                            // If quiet hours are forcing the alarm to be silent,
                            // keep newAlert as false so it will not make noise.
                            if (!forceQuiet) {
                                newAlert = true;
                            }
                        }

                        // Record the received time in the CalendarAlerts table.
                        // This is useful for finding bugs that cause alarms to be
@@ -830,15 +830,15 @@ public class AlertService extends Service {
        }
    }

    void processMessage(Message msg) {
        Bundle bundle = (Bundle) msg.obj;

        // On reboot, update the notification bar with the contents of the
        // CalendarAlerts table.
        String action = bundle.getString("action");
    public static void handleAction(Context context, String action) {
        if (DEBUG) {
            Log.d(TAG, bundle.getLong(android.provider.CalendarContract.CalendarAlerts.ALARM_TIME)
                    + " Action = " + action);
            Log.d(TAG, "Handling action: " + action);
        }

        if (sReceivedProviderReminderBroadcast == null) {
            sReceivedProviderReminderBroadcast = Utils.getSharedPreference(context,
                    PROVIDER_REMINDER_PREF_KEY, false);
        }

        // Some OEMs had changed the provider's EVENT_REMINDER broadcast to their own event,
@@ -847,15 +847,10 @@ public class AlertService extends Service {
        boolean providerReminder = action.equals(
                android.provider.CalendarContract.ACTION_EVENT_REMINDER);
        if (providerReminder) {
            if (sReceivedProviderReminderBroadcast == null) {
                sReceivedProviderReminderBroadcast = Utils.getSharedPreference(this,
                        PROVIDER_REMINDER_PREF_KEY, false);
            }

            if (!sReceivedProviderReminderBroadcast) {
                sReceivedProviderReminderBroadcast = true;
                Log.d(TAG, "Setting key " + PROVIDER_REMINDER_PREF_KEY + " to: true");
                Utils.setSharedPreference(this, PROVIDER_REMINDER_PREF_KEY, true);
                Utils.setSharedPreference(context, PROVIDER_REMINDER_PREF_KEY, true);
            }
        }

@@ -877,83 +872,37 @@ public class AlertService extends Service {
                }
            }

            updateAlertNotification(this);
            updateAlertNotification(context);
        } else if (action.equals(Intent.ACTION_TIME_CHANGED)) {
            doTimeChanged();
            rescheduleAndDisplayTimeChanged(context);
        } else if (action.equals(AlertReceiver.ACTION_DISMISS_OLD_REMINDERS)) {
            dismissOldAlerts(this);
            dismissOldAlerts(context);
        } else {
            Log.w(TAG, "Invalid action: " + action);
        }

        // Schedule the alarm for the next upcoming reminder, if not done by the provider.
        // Scheduling next alarm
        if (sReceivedProviderReminderBroadcast == null || !sReceivedProviderReminderBroadcast) {
            Log.d(TAG, "Scheduling next alarm with AlarmScheduler. "
                    + "sEventReminderReceived: " + sReceivedProviderReminderBroadcast);
            AlarmScheduler.scheduleNextAlarm(this);
            AlarmScheduler.scheduleNextAlarm(context);
        }
    }

    private void doTimeChanged() {
        ContentResolver cr = getContentResolver();
        // TODO Move this into Provider
        rescheduleMissedAlarms(cr, this, AlertUtils.createAlarmManager(this));
        updateAlertNotification(this);
    }

    @Override
    public void onCreate() {
        HandlerThread thread = new HandlerThread("AlertService",
                Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);

        // Flushes old fired alerts from internal storage, if needed.
        AlertUtils.flushOldAlertsFromInternalStorage(getApplication());
    private static void rescheduleAndDisplayTimeChanged(Context context) {
        ContentResolver cr = context.getContentResolver();
        rescheduleMissedAlarms(cr, context, AlertUtils.createAlarmManager(context));
        updateAlertNotification(context);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null) {
    public static Notification buildForegroundNotification(Context context, String title, int iconResId, String channelId) {

            if (Utils.isOreoOrLater()) {
                createChannels(this);
                Notification notification = new NotificationCompat.Builder(this, FOREGROUND_CHANNEL_ID)
                        .setContentTitle(getString(R.string.foreground_notification_title))
                        .setSmallIcon(R.drawable.stat_notify_refresh_events)
        return new NotificationCompat.Builder(context, channelId)
                .setContentTitle(title)
                .setSmallIcon(iconResId)
                .setShowWhen(false)
                .setCategory(Notification.CATEGORY_SERVICE)
                .build();
                if (Utils.isQOrLater()) {
                    int serviceType;
                    if (Utils.isUpsideDownCakeOrLater()) {
                        serviceType = FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED;
                    } else {
                        serviceType = 0;
                    }
                    ServiceCompat.startForeground(this, 1337, notification, serviceType);
                } else {
                    startForeground(1337, notification);
                }
            }

            Message msg = mServiceHandler.obtainMessage();
            msg.arg1 = startId;
            msg.obj = intent.getExtras();
            mServiceHandler.sendMessage(msg);
        }
        return START_REDELIVER_INTENT;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    public static void createChannels(Context context) {
@@ -1108,18 +1057,4 @@ public class AlertService extends Service {
            return retVal;
        }
    }

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            processMessage(msg);
            // NOTE: We MUST not call stopSelf() directly, since we need to
            // make sure the wake lock acquired by AlertReceiver is released.
            AlertReceiver.finishStartingService(AlertService.this, msg.arg1);
        }
    }
}
+30 −0
Original line number Diff line number Diff line
@@ -36,6 +36,11 @@ import android.text.format.DateUtils;
import android.util.Log;

import androidx.core.content.ContextCompat;
import androidx.work.Data;
import androidx.work.ExistingWorkPolicy;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;

import com.android.calendar.EventInfoActivity;
import com.android.calendar.Utils;
import com.android.calendar.calendarcommon2.Time;
@@ -350,4 +355,29 @@ public class AlertUtils {
        timeObj.set(endMillis);
        return Time.getJulianDay(endMillis, timeObj.getGmtOffset()) - startDay;
    }

    /**
     * Queues the AlertWorker with a specific action and a REPLACE policy
     *
     * @param context The application context.
     * @param action The action to be triggered, e.g., Intent.ACTION_TIME_CHANGED.
     */
    public static void scheduleAlertWorker(Context context, String action) {
        // Create input data for the worker
        Data inputData = new Data.Builder()
                .putString(AlertWorker.KEY_ACTION, action)
                .build();

        // Create a work request
        OneTimeWorkRequest alertWorkRequest = new OneTimeWorkRequest.Builder(AlertWorker.class)
                .setInputData(inputData)
                .addTag("alert_processing_work")
                .build();

        // Queue work to avoid duplicates
        WorkManager.getInstance(context).enqueueUniqueWork(
                "CalendarAlertProcessing",
                ExistingWorkPolicy.REPLACE,
                alertWorkRequest);
    }
}
Loading