Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moving import and export to a worker #1801

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ dependencies {
implementation("androidx.preference:preference:1.2.1")
implementation("com.google.android.material:material:1.12.0")
implementation("com.github.yalantis:ucrop:2.2.9")
implementation("androidx.work:work-runtime:2.9.0")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")

// Splash Screen
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="23" />

<uses-feature
Expand Down Expand Up @@ -188,5 +190,6 @@
<action android:name="android.service.controls.ControlsProviderService" />
</intent-filter>
</service>
<service android:name=".importexport.ImportExportWorker"/>
</application>
</manifest>
235 changes: 91 additions & 144 deletions app/src/main/java/protect/card_locker/ImportExportActivity.java

Large diffs are not rendered by default.

143 changes: 0 additions & 143 deletions app/src/main/java/protect/card_locker/ImportExportTask.java

This file was deleted.

74 changes: 73 additions & 1 deletion app/src/main/java/protect/card_locker/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,29 @@
import androidx.appcompat.widget.SearchView;
import androidx.core.splashscreen.SplashScreen;
import androidx.recyclerview.widget.RecyclerView;
import androidx.work.Data;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;

import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayout;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;

import protect.card_locker.databinding.ContentMainBinding;
import protect.card_locker.databinding.MainActivityBinding;
import protect.card_locker.databinding.SortingOptionBinding;
import protect.card_locker.importexport.DataFormat;
import protect.card_locker.importexport.ImportExportWorker;
import protect.card_locker.preferences.SettingsActivity;

public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCardCursorAdapter.CardAdapterListener {
Expand Down Expand Up @@ -71,6 +79,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard

private ActivityResultLauncher<Intent> mBarcodeScannerLauncher;
private ActivityResultLauncher<Intent> mSettingsLauncher;
private ActivityResultLauncher<Intent> mImportExportLauncher;

private ActionMode.Callback mCurrentActionModeCallback = new ActionMode.Callback() {
@Override
Expand Down Expand Up @@ -304,6 +313,69 @@ public void onClick(DialogInterface dialog, int whichButton) {
}
});

mImportExportLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
// User didn't ask for import or export
if (result.getResultCode() != RESULT_OK) {
return;
}

// Watch for active imports/exports
new Thread(() -> {
WorkManager workManager = WorkManager.getInstance(MainActivity.this);

Snackbar importRunning = Snackbar.make(binding.getRoot(), R.string.importing, Snackbar.LENGTH_INDEFINITE);

while (true) {
try {
List<WorkInfo> activeImports = workManager.getWorkInfosForUniqueWork(ImportExportWorker.ACTION_IMPORT).get();

// We should only have one import running at a time, so it should be safe to always grab the latest
WorkInfo activeImport = activeImports.get(activeImports.size() - 1);
WorkInfo.State importState = activeImport.getState();

if (importState == WorkInfo.State.RUNNING || importState == WorkInfo.State.ENQUEUED || importState == WorkInfo.State.BLOCKED) {
importRunning.show();
} else if (importState == WorkInfo.State.SUCCEEDED) {
importRunning.dismiss();
runOnUiThread(() -> {
Toast.makeText(getApplicationContext(), getString(R.string.importSuccessful), Toast.LENGTH_LONG).show();
updateLoyaltyCardList(true);
});

break;
} else {
importRunning.dismiss();

Data outputData = activeImport.getOutputData();

// FIXME: This dialog will asynchronously be accepted or declined and we don't know the status of it so we can't show the import state
// We want to get back into this function
// A cheap fix would be to keep looping but if the user dismissed the dialog that could mean we're looping forever...
if (Objects.equals(outputData.getString(ImportExportWorker.OUTPUT_ERROR_REASON), ImportExportWorker.ERROR_PASSWORD_REQUIRED)) {
runOnUiThread(() -> ImportExportActivity.retryWithPassword(
MainActivity.this,
DataFormat.valueOf(outputData.getString(ImportExportWorker.INPUT_FORMAT)),
Uri.parse(outputData.getString(ImportExportWorker.INPUT_URI))
));
} else {
runOnUiThread(() -> {
Toast.makeText(getApplicationContext(), getString(R.string.importFailed), Toast.LENGTH_LONG).show();
Toast.makeText(getApplicationContext(), activeImport.getOutputData().getString(ImportExportWorker.OUTPUT_ERROR_REASON), Toast.LENGTH_LONG).show();
Toast.makeText(getApplicationContext(), activeImport.getOutputData().getString(ImportExportWorker.OUTPUT_ERROR_DETAILS), Toast.LENGTH_LONG).show();
});
}

break;
}
} catch (ExecutionException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
});

getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
Expand Down Expand Up @@ -641,7 +713,7 @@ public boolean onOptionsItemSelected(MenuItem inputItem) {

if (id == R.id.action_import_export) {
Intent i = new Intent(getApplicationContext(), ImportExportActivity.class);
startActivity(i);
mImportExportLauncher.launch(i);
return true;
}

Expand Down
63 changes: 63 additions & 0 deletions app/src/main/java/protect/card_locker/NotificationHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package protect.card_locker;

import static android.content.Context.NOTIFICATION_SERVICE;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class NotificationHelper {

// Do not change these IDs!
public static final String CHANNEL_IMPORT = "import";

public static final String CHANNEL_EXPORT = "export";

public static final int IMPORT_ID = 100;
public static final int IMPORT_PROGRESS_ID = 101;
public static final int EXPORT_ID = 103;
public static final int EXPORT_PROGRESS_ID = 104;


public static Notification.Builder createNotificationBuilder(@NonNull Context context, @NonNull String channel, @NonNull int icon, @NonNull String title, @Nullable String message) {
Notification.Builder notificationBuilder = new Notification.Builder(context)
.setSmallIcon(icon)
.setTicker(title)
.setContentTitle(title);

if (message != null) {
notificationBuilder.setContentText(message);
}

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
NotificationChannel notificationChannel = new NotificationChannel(channel, getChannelName(channel), NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(notificationChannel);

notificationBuilder.setChannelId(channel);
}

return notificationBuilder;
}

public static void sendNotification(@NonNull Context context, @NonNull int notificationId, @NonNull Notification notification) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);

notificationManager.notify(notificationId, notification);
}

private static String getChannelName(@NonNull String channel) {
switch(channel) {
case CHANNEL_IMPORT:
return "Import";
case CHANNEL_EXPORT:
return "Export";
default:
throw new IllegalArgumentException("Unknown notification channel");
}
}
}
Loading
Loading