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

Commit f7137d7b authored by Vincent Bourgmayer's avatar Vincent Bourgmayer
Browse files

Merge branch '299-step-1-remove-initializerService' into 'main'

[eDrive][persisted=false] STEP 1: replace InitializerService by Receivers

See merge request !254
parents 09ed84b1 2d7373f0
Loading
Loading
Loading
Loading
Loading
+13 −12
Original line number Diff line number Diff line
@@ -28,6 +28,9 @@
        android:protectionLevel="signature"
        tools:ignore="ReservedSystemPermission" />

    <permission android:name="foundation.e.permission.ADD_ACCOUNT"
        android:protectionLevel="signature" />

    <application
        android:name=".EdriveApplication"
        android:allowBackup="true"
@@ -80,17 +83,6 @@
            android:exported="true"
            android:label="@string/account_setting_metered_network"
            tools:ignore="ExportedContentProvider" />

        <!-- Services -->
        <service
            android:name=".services.InitializerService"
            android:enabled="true"
            android:exported="true"
            tools:ignore="ExportedService">
            <intent-filter>
                <action android:name="drive.services.InitializerService" />
            </intent-filter>
        </service>
        <service
            android:name=".services.ObserverService"
            android:enabled="true" />
@@ -115,12 +107,21 @@
            </intent-filter>
        </receiver>

        <receiver android:name=".receivers.AccountRemoveCallbackReceiver"
        <receiver android:name=".account.receivers.AccountRemoveCallbackReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.accounts.action.ACCOUNT_REMOVED"/>
            </intent-filter>
        </receiver>

        <receiver android:name=".account.receivers.AccountAddedReceiver"
            android:enabled="true"
            android:permission="foundation.e.permission.ADD_ACCOUNT"
            android:exported="true">
            <intent-filter>
                <action android:name="foundation.e.drive.action.ADD_ACCOUNT"/>
            </intent-filter>
        </receiver>
    </application>
</manifest>
 No newline at end of file
+136 −0
Original line number Diff line number Diff line
/*
 * Copyright © MURENA SAS 2023.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 */
package foundation.e.drive.account.receivers

import android.accounts.AccountManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import foundation.e.drive.R
import foundation.e.drive.models.SyncedFolder
import foundation.e.drive.utils.AppConstants
import foundation.e.drive.utils.CommonUtils
import foundation.e.drive.utils.DavClientProvider
import foundation.e.drive.utils.RootSyncedFolderProvider
import foundation.e.drive.work.WorkRequestFactory.*
import timber.log.Timber

/**
 * Entry point for eDrive
 * Triggered by AccountManager
 * @author Vincent Bourgmayer
 */
class AccountAddedReceiver() : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        Timber.d("\"Account added\" intent received")

        if (context == null || intent == null || intent.extras == null) return

        val extras = intent.extras!!
        val accountName = extras.getString(AccountManager.KEY_ACCOUNT_NAME, "")
        val accountType = extras.getString(AccountManager.KEY_ACCOUNT_TYPE, "")
        val prefs = context.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME,
            Context.MODE_PRIVATE)

        Timber.d("AccountAddedReceiver.onReceive with name: %s and type: %s", accountName, accountType)

        if (!canStart(accountName, accountType, prefs, context)) return

        prefs.edit()
            .putString(AccountManager.KEY_ACCOUNT_NAME, accountName)
            .apply()

        if (registerSetupWorkers(context)) {
            DavClientProvider.getInstance().cleanUp()

            CommonUtils.registerPeriodicUserInfoChecking(WorkManager.getInstance(context))
        }
}

    /**
     * Check that conditions to start are met:
     * - Setup has not already been done
     * - AccountName is not empty
     * - AccountType is /e/ account
     * - the account is effectively available through accountManager
     */
    private fun canStart(
        accountName: String,
        accountType: String,
        prefs: SharedPreferences,
        context: Context
    ): Boolean {
        if (isSetupAlreadyDone(prefs)) {
            return false
        }

        if (accountName.isEmpty()) {
            return false
        }

        if (isInvalidAccountType(accountType, context)) {
            return false
        }

        if (!isExistingAccount(accountName, accountType, context)) {
            Timber.w("No account exist for username: %s ", accountType, accountName)
            return false
        }
        return true
    }

    private fun isInvalidAccountType(accountType: String, context: Context): Boolean {
        val validAccountType = context.getString(R.string.eelo_account_type)
        return accountType != validAccountType
    }

    private fun isSetupAlreadyDone(prefs: SharedPreferences): Boolean {
        return prefs.getBoolean(AppConstants.SETUP_COMPLETED, false)
    }

    private fun isExistingAccount(accountName: String, accountType: String, context: Context): Boolean {
        val account = CommonUtils.getAccount(accountName, accountType, AccountManager.get(context))
        return account != null
    }

    private fun registerSetupWorkers(context: Context): Boolean {
        val rootFolderSetupWorkers = generateRootFolderSetupWorkers(context) ?: return false
        val getUserInfoRequest = getOneTimeWorkRequest(WorkType.ONE_TIME_USER_INFO, null)
        val firstStartRequest = getOneTimeWorkRequest(WorkType.FIRST_START, null)

        val workManager = WorkManager.getInstance(context)

        workManager.beginWith(getUserInfoRequest)
            .then(rootFolderSetupWorkers)
            .then(firstStartRequest)
            .enqueue()
        return true
    }

    private fun generateRootFolderSetupWorkers(context: Context): MutableList<OneTimeWorkRequest>? {
        val rootSyncedFolderList: List<SyncedFolder> =
            RootSyncedFolderProvider.getSyncedFolderRoots(context)

        if (rootSyncedFolderList.isNullOrEmpty()) {
            return null
        }

        val workRequests: MutableList<OneTimeWorkRequest> = ArrayList()
        for (folder in rootSyncedFolderList) {
            val createRemoteFolderWorkRequest = getOneTimeWorkRequest(
                WorkType.CREATE_REMOTE_DIR,
                folder
            )
            workRequests.add(createRemoteFolderWorkRequest)
        }
        return workRequests
    }
}
 No newline at end of file
+3 −8
Original line number Diff line number Diff line
@@ -6,10 +6,10 @@
 * http://www.gnu.org/licenses/gpl.html
 */

package foundation.e.drive.receivers;
package foundation.e.drive.account.receivers;

import static foundation.e.drive.utils.AppConstants.INITIAL_FOLDER_NUMBER;
import static foundation.e.drive.utils.AppConstants.INITIALIZATION_HAS_BEEN_DONE;
import static foundation.e.drive.utils.AppConstants.SETUP_COMPLETED;

import android.accounts.AccountManager;
import android.annotation.SuppressLint;
@@ -26,7 +26,6 @@ import java.io.File;
import foundation.e.drive.EdriveApplication;
import foundation.e.drive.database.DbHelper;
import foundation.e.drive.database.FailedSyncPrefsManager;
import foundation.e.drive.services.InitializerService;
import foundation.e.drive.services.ObserverService;
import foundation.e.drive.services.SynchronizationService;
import foundation.e.drive.utils.AppConstants;
@@ -100,10 +99,6 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver {
        boolean observerServiceStopResult = applicationContext.stopService(observerServiceIntent);
        Timber.d("stop ObserverService: %s", observerServiceStopResult);

        Intent initializerServiceIntent = new Intent(applicationContext, InitializerService.class);
        boolean initServiceStopResult = applicationContext.stopService(initializerServiceIntent);
        Timber.d("stop InitializerService: %s", initServiceStopResult);

        Intent synchronizationServiceIntent = new Intent(applicationContext, SynchronizationService.class);
        boolean syncServiceStopResult = applicationContext.stopService(synchronizationServiceIntent);
        Timber.d("stop SynchronizationService: %s", syncServiceStopResult);
@@ -114,7 +109,7 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver {
            //If removal failed, clear all data inside
            prefs.edit().remove(AccountManager.KEY_ACCOUNT_NAME)
                    .remove(AccountManager.KEY_ACCOUNT_TYPE)
                    .remove(INITIALIZATION_HAS_BEEN_DONE)
                    .remove(SETUP_COMPLETED)
                    .remove(INITIAL_FOLDER_NUMBER)
                    .remove(AppConstants.KEY_LAST_SYNC_TIME)
                    .apply();
+5 −13
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ import androidx.annotation.NonNull;

import foundation.e.drive.BuildConfig;
import foundation.e.drive.database.DbHelper;
import foundation.e.drive.services.InitializerService;
import foundation.e.drive.utils.AppConstants;
import foundation.e.drive.utils.CommonUtils;
import timber.log.Timber;
@@ -39,11 +38,11 @@ public class BootCompletedReceiver extends BroadcastReceiver {
        if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
            final String currentDateProp = CommonUtils.getProp(DATE_SYSTEM_PROPERTY);

            if (isOsUpdated(pref, currentDateProp)) { // App is persistent and system so do not have classical update
                handleOsUpdate(pref, context, currentDateProp);
            if (isOsUpdated(pref, currentDateProp)) { // App is persistent so can only be updated (by replacement) on OS update
                handleOsUpdate(context);
            }

            if (pref.getBoolean(AppConstants.INITIALIZATION_HAS_BEEN_DONE, false)
            if (pref.getBoolean(AppConstants.SETUP_COMPLETED, false)
                    && BuildConfig.VERSION_CODE > pref.getInt(PREF_VERSION_CODE, 1002000)) {
                pref.edit().putInt(PREF_VERSION_CODE, BuildConfig.VERSION_CODE).apply();
                try {
@@ -62,17 +61,10 @@ public class BootCompletedReceiver extends BroadcastReceiver {

    /**
     * Force reinitialization, upgrade of DB in case of OS update
     *
     * @param prefs SharedPreferences to extract last known build date
     * todo remove when setPersistentFlag=true will be removed
     * @param context Context used to start InitializationService
     * @param currentDateProp new build date
     */
    private void handleOsUpdate(@NonNull SharedPreferences prefs, @NonNull Context context, @NonNull String currentDateProp) {
        prefs.edit().putString(DATE_SYSTEM_PROPERTY, currentDateProp)
                .putBoolean(AppConstants.INITIALIZATION_HAS_BEEN_DONE, false)
                .apply();

        context.startService(new Intent(context, InitializerService.class));
    private void handleOsUpdate(@NonNull Context context) {
        forceDBUpdate(context);
    }

+0 −123
Original line number Diff line number Diff line
/*
 * Copyright © CLEUS SAS 2018-2019.
 * Copyright © ECORP SAS 2022-2023.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 */

package foundation.e.drive.services;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.IBinder;

import java.io.File;
import java.util.List;

import foundation.e.drive.models.SyncedFolder;
import foundation.e.drive.utils.AppConstants;
import foundation.e.drive.utils.CommonUtils;
import foundation.e.drive.utils.DavClientProvider;
import foundation.e.drive.utils.RootSyncedFolderProvider;
import timber.log.Timber;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.work.WorkManager;

/**
 * @author Vincent Bourgmayer
 * @author Jonathan Klee
 * @author Abhishek Aggarwal
 */
public class InitializerService extends Service {
    private Account account;

    @Override
    public void onCreate() {
        Timber.tag(InitializerService.class.getSimpleName());
        super.onCreate();
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Timber.i("onStartCommand()");
        DavClientProvider.getInstance().cleanUp();
        CommonUtils.setServiceUnCaughtExceptionHandler(this);

        //Get account
        final SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE);

        String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, "");
        String accountType = prefs.getString(AccountManager.KEY_ACCOUNT_TYPE, "");

        if (accountName.isEmpty() && accountType.isEmpty() && intent != null && intent.getExtras() != null) {

            accountName = intent.getExtras().getString(AccountManager.KEY_ACCOUNT_NAME, "");
            accountType = intent.getExtras().getString(AccountManager.KEY_ACCOUNT_TYPE, "");

            prefs.edit().putString(AccountManager.KEY_ACCOUNT_NAME, accountName)
                    .putString(AccountManager.KEY_ACCOUNT_TYPE, accountType)
                    .apply();
        }

        if (checkStartConditions(prefs, accountName, accountType)) {
            start();
        }
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * Check if condition are present to start
     * - Initialization not already done
     * - AccountName is not empty
     * - Account available
     * @return true if condition are met
     */
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    public boolean checkStartConditions(@NonNull final SharedPreferences prefs,
             @NonNull final String accountName, @NonNull final String accountType) {
        if (prefs.getBoolean(AppConstants.INITIALIZATION_HAS_BEEN_DONE, false)) {
            Timber.w("Initialization has already been done");
            return false;
        }

        if (accountName.isEmpty()) {
            Timber.w("No account Name available");
            return false;
        }

        account = CommonUtils.getAccount(accountName, accountType, AccountManager.get(this));
        if (account == null) {
            Timber.w("got Invalid %s account for username: %s ", accountType, accountName);
            return false;
        }
        return true;
    }

    /**
     * Set up base component for eDrive:
     * - Register basic worker
     * - build root folders to sync
     */
    private void start() {
        Timber.d("start()");
        CommonUtils.registerPeriodicUserInfoChecking(WorkManager.getInstance(this));

        final List<SyncedFolder> syncedFolders = RootSyncedFolderProvider.INSTANCE.getSyncedFolderRoots(getApplicationContext());
        CommonUtils.registerInitializationWorkers(syncedFolders, WorkManager.getInstance(getApplicationContext()) );
    }

    @Nullable
    @Override
    public IBinder onBind(@Nullable Intent intent) {
        return null;
    }
}
 No newline at end of file
Loading