diff --git a/app/src/main/java/com/iyxan23/sketch/collab/online/BrowseActivity.java b/app/src/main/java/com/iyxan23/sketch/collab/online/BrowseActivity.java index 318d02f..8c0409e 100644 --- a/app/src/main/java/com/iyxan23/sketch/collab/online/BrowseActivity.java +++ b/app/src/main/java/com/iyxan23/sketch/collab/online/BrowseActivity.java @@ -147,9 +147,10 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { if (savedInstanceState != null) savedInstanceState.putParcelableArrayList("items", items); - findViewById(R.id.progressBar_browse).setVisibility(View.GONE); - - runOnUiThread(() -> adapter.updateView(items)); + runOnUiThread(() -> { + findViewById(R.id.progressBar_browse).setVisibility(View.GONE); + adapter.updateView(items); + }); }).start(); } } diff --git a/app/src/main/java/com/iyxan23/sketch/collab/online/ViewOnlineProjectActivity.java b/app/src/main/java/com/iyxan23/sketch/collab/online/ViewOnlineProjectActivity.java index ee329a8..fec77b6 100644 --- a/app/src/main/java/com/iyxan23/sketch/collab/online/ViewOnlineProjectActivity.java +++ b/app/src/main/java/com/iyxan23/sketch/collab/online/ViewOnlineProjectActivity.java @@ -23,6 +23,7 @@ import android.view.View; import android.widget.TextView; +import android.widget.Toast; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.firestore.CollectionReference; @@ -181,6 +182,17 @@ public void commitsOnClick(View v) { // onClick for the "Clone" button public void cloneOnClick(View v) { + AlertDialog.Builder exists_dialog_builder = new AlertDialog.Builder(this); + exists_dialog_builder.setCancelable(true); + exists_dialog_builder.setTitle("Duplicate Project"); + exists_dialog_builder.setMessage("This project already exists in your device (ID: " + project_key + "), are you sure you want to continue?"); + exists_dialog_builder.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss()); + exists_dialog_builder.setPositiveButton("Yes", (dialog, which) -> { + dialog.dismiss(); + do_clone(); + }); + AlertDialog exists_dialog = exists_dialog_builder.create(); + new Thread(() -> { boolean already_exists = false; ArrayList projects = Util.fetch_sketchware_projects(); @@ -199,17 +211,11 @@ public void cloneOnClick(View v) { } if (already_exists) { - AlertDialog.Builder exists_dialog = new AlertDialog.Builder(this); - exists_dialog.setCancelable(true); - exists_dialog.setTitle("Duplicate Project"); - exists_dialog.setMessage("This project already exists in your device (ID: " + project_key + "), are you sure you want to continue?"); - exists_dialog.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss()); - exists_dialog.setPositiveButton("Yes", (dialog, which) -> { - dialog.dismiss(); - do_clone(); - }); - - exists_dialog.create().show(); + runOnUiThread(exists_dialog::show); + } else { + // Clone right away + // Probably will add a confirmation dialog + do_clone(); } }).start(); } diff --git a/app/src/main/java/com/iyxan23/sketch/collab/services/CloneService.java b/app/src/main/java/com/iyxan23/sketch/collab/services/CloneService.java index db81e18..a6c4e42 100644 --- a/app/src/main/java/com/iyxan23/sketch/collab/services/CloneService.java +++ b/app/src/main/java/com/iyxan23/sketch/collab/services/CloneService.java @@ -6,28 +6,26 @@ import android.app.PendingIntent; import android.app.Service; import android.content.Intent; -import android.os.Process; import android.os.Build; -import android.os.Handler; -import android.os.HandlerThread; import android.os.IBinder; -import android.os.Looper; -import android.os.Message; +import android.util.Log; import android.widget.Toast; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import com.google.android.gms.tasks.Tasks; import com.google.firebase.firestore.CollectionReference; import com.google.firebase.firestore.DocumentReference; import com.google.firebase.firestore.DocumentSnapshot; import com.google.firebase.firestore.FirebaseFirestore; -import com.google.firebase.firestore.QueryDocumentSnapshot; +import com.google.firebase.firestore.Query; import com.google.firebase.firestore.QuerySnapshot; import com.iyxan23.sketch.collab.R; import com.iyxan23.sketch.collab.Util; import com.iyxan23.sketch.collab.models.Commit; import com.iyxan23.sketch.collab.models.SketchwareProject; +import com.iyxan23.sketch.collab.online.ViewOnlineProjectActivity; import org.json.JSONException; import org.json.JSONObject; @@ -41,12 +39,11 @@ public class CloneService extends Service { + private static final String TAG = "CloneService"; + private final String CLONE_CHANNEL_ID = "CLONE_CHANNEL"; private final int NOTIFICATION_ID = 1; - private Looper serviceLooper; - private ServiceHandler serviceHandler; - String project_key; String project_name; @@ -56,16 +53,46 @@ public IBinder onBind(Intent intent) { return null; } - // Handler that receives messages from the thread - private final class ServiceHandler extends Handler { - public ServiceHandler(Looper looper) { - super(looper); + @Override + public void onCreate() { + super.onCreate(); + } + + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + project_key = intent.getStringExtra("project_key"); + project_name = intent.getStringExtra("project_name"); + + Intent notificationIntent = new Intent(this, ViewOnlineProjectActivity.class); + notificationIntent.putExtra("project_key", project_key); + PendingIntent pendingIntent = + PendingIntent.getActivity(this, 0, notificationIntent, 0); + + final Notification.Builder notification_builder; + + // Channel ID is for 26+ / Android 8+ / Android Oreo+ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createCloneNotificationChannel(); + + notification_builder = new Notification.Builder(this, CLONE_CHANNEL_ID); + } else { + notification_builder = new Notification.Builder(this); } - @Override - public void handleMessage(Message msg) { - // Normally we would do some work here, like download a file. - // For our sample, we just sleep for 5 seconds. + Notification notification = notification_builder + .setContentTitle("Cloning Project") + .setContentText("Cloning " + project_name + " (" + project_key + ")") + .setProgress(100, 1, true) + .setSmallIcon(R.drawable.ic_file_download) + .setContentIntent(pendingIntent) + .build(); + + startForeground(NOTIFICATION_ID, notification); + + // Start the cloning on a different thread + Toast.makeText(CloneService.this, "Cloning started.", Toast.LENGTH_SHORT).show(); + new Thread(() -> { try { FirebaseFirestore firestore = FirebaseFirestore.getInstance(); DocumentReference project = firestore.collection("projects").document(project_key); @@ -80,7 +107,7 @@ public void handleMessage(Message msg) { // Get the snapshot, get the commits, and apply the commits to the snapshot DocumentSnapshot project_metadata = Tasks.await(project.get()); QuerySnapshot snapshot = Tasks.await(project_snapshot.get()); - QuerySnapshot commits = Tasks.await(project_commits .get()); + QuerySnapshot commits = Tasks.await(project_commits .orderBy("timestamp", Query.Direction.ASCENDING).get()); for (DocumentSnapshot doc: snapshot.getDocuments()) { project_data.put(doc.getId(), Util.decrypt(doc.getBlob("data").toBytes())); @@ -89,12 +116,16 @@ public void handleMessage(Message msg) { diff_match_patch dmp = new diff_match_patch(); // Apply the patch for (DocumentSnapshot commit: commits) { - Commit c = commit.toObject(Commit.class); + HashMap patch = (HashMap) commit.get("patch"); + + if (patch == null) continue; for (String key: keys) { - LinkedList patches = (LinkedList) dmp.patch_fromText(c.patch.get(key)); + if (!patch.containsKey(key)) continue; + + LinkedList patches = (LinkedList) dmp.patch_fromText(patch.get(key)); // TODO: CHECK PATCH STATUSES - Object[] result = dmp.patch_apply(patches, c.patch.get(key)); + Object[] result = dmp.patch_apply(patches, patch.get(key)); project_data.put(key, (String) result[0]); } @@ -154,49 +185,10 @@ public void handleMessage(Message msg) { Thread.currentThread().interrupt(); } - Toast.makeText(CloneService.this, "Cloning " + project_name + " finished", Toast.LENGTH_SHORT).show(); - // Stop the service using the startId, so that we don't stop // the service in the middle of handling another job - stopSelf(msg.arg1); - } - } - - @Override - public void onCreate() { - // Start up the thread running the service. Note that we create a - // separate thread because the service normally runs in the process's - // main thread, which we don't want to block. We also make it - // background priority so CPU-intensive work doesn't disrupt our UI. - HandlerThread thread = new HandlerThread("aaa", - Process.THREAD_PRIORITY_BACKGROUND); - thread.start(); - - // Get the HandlerThread's Looper and use it for our Handler - serviceLooper = thread.getLooper(); - serviceHandler = new ServiceHandler(serviceLooper); - } - - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - project_key = intent.getStringExtra("project_key"); - project_name = intent.getStringExtra("project_name"); - - Intent notificationIntent = new Intent(this, this.getClass()); - PendingIntent pendingIntent = - PendingIntent.getActivity(this, 0, notificationIntent, 0); - - Notification notification = - new Notification.Builder(this) - .setContentTitle("Cloning Project") - .setContentText("Cloning " + project_name + " (" + project_key + ")") - .setProgress(100, 1, true) - .setSmallIcon(R.drawable.ic_file_download) - .setContentIntent(pendingIntent) - .build(); - - startForeground(NOTIFICATION_ID, notification); + stopSelf(); + }).start(); return START_NOT_STICKY; } @@ -206,19 +198,18 @@ public void onDestroy() { super.onDestroy(); } - private void createNotificationChannel() { + @RequiresApi(Build.VERSION_CODES.O) + private void createCloneNotificationChannel() { // Create the NotificationChannel, but only on API 26+ because // the NotificationChannel class is new and not in the support library - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - CharSequence name = "Clone Project"; - String description = ""; - int importance = NotificationManager.IMPORTANCE_DEFAULT; - NotificationChannel channel = new NotificationChannel(CLONE_CHANNEL_ID, name, importance); - channel.setDescription(description); - // Register the channel with the system; you can't change the importance - // or other notification behaviors after this - NotificationManager notificationManager = getSystemService(NotificationManager.class); - notificationManager.createNotificationChannel(channel); - } + CharSequence name = "Clone Project"; + String description = "This notification will appear when you clone a project in SketchCollab."; + int importance = NotificationManager.IMPORTANCE_DEFAULT; + NotificationChannel channel = new NotificationChannel(CLONE_CHANNEL_ID, name, importance); + channel.setDescription(description); + // Register the channel with the system; you can't change the importance + // or other notification behaviors after this + NotificationManager notificationManager = getSystemService(NotificationManager.class); + notificationManager.createNotificationChannel(channel); } }