/* * Catroid: An on-device visual programming system for Android devices * Copyright (C) 2010-2016 The Catrobat Team * (<http://developer.catrobat.org/credits>) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * An additional term exception under section 7 of the GNU Affero * General Public License, version 3, is available at * http://developer.catrobat.org/license_additional_term * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.catrobat.catroid.utils; import android.app.IntentService; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.media.RingtoneManager; import android.net.Uri; import android.os.Bundle; import android.os.ResultReceiver; import android.util.Log; import android.util.SparseArray; import org.catrobat.catroid.R; import org.catrobat.catroid.common.Constants; import org.catrobat.catroid.transfers.ProjectUploadService; import org.catrobat.catroid.ui.MainMenuActivity; public final class StatusBarNotificationManager { public static final String EXTRA_PROJECT_NAME = "projectName"; public static final int MAXIMUM_PERCENT = 100; private static final String TAG = StatusBarNotificationManager.class.getSimpleName(); public static final String ACTION_UPDATE_POCKET_CODE_VERSION = "update_pocket_code_version"; public static final String ACTION_RETRY_UPLOAD = "retry_upload"; public static final String ACTION_CANCEL_UPLOAD = "cancel_upload"; private static final StatusBarNotificationManager INSTANCE = new StatusBarNotificationManager(); private int notificationId; private SparseArray<NotificationData> notificationDataMap = new SparseArray<NotificationData>(); private Context context; private NotificationManager notificationManager; private int progressPercent; private StatusBarNotificationManager() { } public static StatusBarNotificationManager getInstance() { return INSTANCE; } private void initNotificationManager(Context context) { if (notificationManager == null) { notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); } this.context = context; } public int createUploadNotification(Context context, String programName) { if (context == null || programName == null) { return -1; } initNotificationManager(context); Intent uploadIntent = new Intent(context, MainMenuActivity.class); uploadIntent.setAction(Intent.ACTION_MAIN); uploadIntent = uploadIntent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); PendingIntent pendingIntent = PendingIntent.getActivity(context, notificationId, uploadIntent, PendingIntent.FLAG_CANCEL_CURRENT); NotificationData data = new NotificationData(context, pendingIntent, R.drawable.ic_stat, programName, R.string.notification_upload_title_pending, R.string.notification_upload_title_finished, R.string.notification_upload_pending, R.string.notification_upload_finished); int id = createNotification(context, data); showOrUpdateNotification(id, 0); return id; } public int createCopyNotification(Context context, String programName) { if (context == null || programName == null) { return -1; } initNotificationManager(context); Intent copyIntent = new Intent(context, MainMenuActivity.class); copyIntent.setAction(Intent.ACTION_MAIN).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) .putExtra(EXTRA_PROJECT_NAME, programName); PendingIntent pendingIntent = PendingIntent.getActivity(context, notificationId, copyIntent, PendingIntent.FLAG_CANCEL_CURRENT); NotificationData data = new NotificationData(context, pendingIntent, R.drawable.ic_stat, programName, R.string.notification_copy_title_pending, R.string.notification_title_open, R.string.notification_copy_pending, R.string.notification_copy_finished); int id = createNotification(context, data); showOrUpdateNotification(id, 0); return id; } public int createDownloadNotification(Context context, String programName) { if (context == null || programName == null) { return -1; } initNotificationManager(context); Intent downloadIntent = new Intent(context, MainMenuActivity.class); downloadIntent.setAction(Intent.ACTION_MAIN).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) .putExtra(EXTRA_PROJECT_NAME, programName); PendingIntent pendingIntent = PendingIntent.getActivity(context, notificationId, downloadIntent, PendingIntent.FLAG_CANCEL_CURRENT); NotificationData data = new NotificationData(context, pendingIntent, R.drawable.ic_stat, programName, R.string.notification_download_title_pending, R.string.notification_title_open, R.string.notification_download_pending, R.string.notification_download_finished); return createNotification(context, data); } private int createNotification(Context context, NotificationData data) { initNotificationManager(context); PendingIntent doesNothingPendingIntent = PendingIntent.getActivity(context, -1, new Intent(), PendingIntent.FLAG_ONE_SHOT); Notification.Builder notificationBuilder = new Notification.Builder(context); notificationBuilder.setContentTitle(data.getNotificationTitleWorking()) .setContentText(data.getNotificationTextWorking()).setSmallIcon(data.getNotificationIcon()) .setOngoing(true).setContentIntent(doesNothingPendingIntent); data.setNotificationBuilder(notificationBuilder); notificationDataMap.put(notificationId, data); return notificationId++; } public void showOrUpdateNotification(int id, int progressInPercent) { NotificationData notificationData = notificationDataMap.get(id); if (notificationData == null) { return; } progressPercent = progressInPercent; Notification.Builder notificationBuilder = notificationData.getNotificationBuilder(); notificationBuilder.setProgress(100, progressInPercent, false); notificationManager.notify(id, notificationBuilder.build()); if (progressInPercent == 100) { notificationBuilder.setContentTitle(notificationData.getNotificationTitleDone()) .setContentText(notificationData.getNotificationTextDone()).setProgress(0, 0, false) .setAutoCancel(true).setContentIntent(notificationData.getPendingIntent()).setOngoing(false); notificationManager.notify(id, notificationBuilder.build()); } } public void abortProgressNotificationWithMessage(int id, String changeDoneText) { NotificationData notificationData = notificationDataMap.get(id); if (notificationData == null) { return; } notificationData.setNotificationTextDone(changeDoneText); notificationDataMap.put(id, notificationData); showOrUpdateNotification(id, MAXIMUM_PERCENT); } public void showUploadRejectedNotification(int id, int statusCode, String serverAnswer, Bundle bundle) { NotificationData notificationData = notificationDataMap.get(id); if (notificationData == null) { Log.d(TAG, "NotificationData is null."); return; } Notification.Builder notificationBuilder = notificationData.getNotificationBuilder(); notificationData.setNotificationTextDone(serverAnswer); Uri alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); notificationBuilder.setContentTitle(context.getResources().getText(R.string.notification_upload_rejected)) .setContentText(serverAnswer) .setTicker(context.getResources().getText(R.string.notification_upload_rejected)) .setSound(alarmSound) .setStyle(new Notification.BigTextStyle().bigText(serverAnswer)) .setProgress(0, 0, false) .setAutoCancel(true) .setPriority(Notification.PRIORITY_MAX) .setOngoing(false); switch (statusCode) { case Constants.STATUS_CODE_INTERNAL_SERVER_ERROR: case Constants.STATUS_CODE_UPLOAD_MISSING_DATA: case Constants.STATUS_CODE_UPLOAD_INVALID_CHECKSUM: case Constants.STATUS_CODE_UPLOAD_COPY_FAILED: case Constants.STATUS_CODE_UPLOAD_UNZIP_FAILED: case Constants.STATUS_CODE_UPLOAD_MISSING_XML: case Constants.STATUS_CODE_UPLOAD_RENAME_FAILED: case Constants.STATUS_CODE_UPLOAD_SAVE_THUMBNAIL_FAILED: Intent actionIntentRetryUpload = new Intent(context, NotificationActionService.class) .setAction(ACTION_RETRY_UPLOAD); actionIntentRetryUpload.putExtra("bundle", bundle); PendingIntent actionPendingIntentRetryUpload = PendingIntent.getService(context, id, actionIntentRetryUpload, PendingIntent.FLAG_CANCEL_CURRENT); notificationBuilder.addAction(android.R.drawable.ic_popup_sync, context.getResources().getString(R.string.notification_upload_retry), actionPendingIntentRetryUpload); Intent actionIntentCancelUpload = new Intent(context, NotificationActionService.class) .setAction(ACTION_CANCEL_UPLOAD); actionIntentCancelUpload.putExtra("bundle", bundle); PendingIntent actionPendingIntentCancelUpload = PendingIntent.getService(context, id, actionIntentCancelUpload, PendingIntent.FLAG_ONE_SHOT); notificationBuilder.addAction(android.R.drawable.ic_menu_close_clear_cancel, context.getResources().getString(R.string.cancel), actionPendingIntentCancelUpload); break; case Constants.STATUS_CODE_UPLOAD_MISSING_CHECKSUM: case Constants.STATUS_CODE_UPLOAD_OLD_CATROBAT_LANGUAGE: case Constants.STATUS_CODE_UPLOAD_OLD_CATROBAT_VERSION: Intent actionIntentUpdatePocketCodeVersion = new Intent(context, NotificationActionService.class) .setAction(ACTION_UPDATE_POCKET_CODE_VERSION) .putExtra("notificationId", id); PendingIntent actionPendingIntentUpdatePocketCodeVersion = PendingIntent.getService(context, id, actionIntentUpdatePocketCodeVersion, PendingIntent.FLAG_ONE_SHOT); notificationBuilder.addAction(R.drawable.ic_launcher, context.getResources().getString(R.string.notification_open_play_store), actionPendingIntentUpdatePocketCodeVersion); break; default: notificationDataMap.put(id, notificationData); Intent openIntent = new Intent(context, MainMenuActivity.class); openIntent.setAction(Intent.ACTION_MAIN).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) .putExtra(EXTRA_PROJECT_NAME, bundle.getString("projectName")); PendingIntent pendingIntent = PendingIntent.getActivity(context, notificationId, openIntent, PendingIntent.FLAG_CANCEL_CURRENT); notificationData.setPendingIntent(pendingIntent); showOrUpdateNotification(id, MAXIMUM_PERCENT); return; } notificationBuilder.setContentIntent(notificationData.getPendingIntent()); notificationDataMap.put(id, notificationData); notificationManager.notify(id, notificationBuilder.build()); } public void cancelNotification(int id) { notificationDataMap.remove(id); notificationManager.cancel(id); } public int getProgressPercent() { return progressPercent; } public static class NotificationActionService extends IntentService { public NotificationActionService() { super(NotificationActionService.class.getSimpleName()); } @Override protected void onHandleIntent(Intent intent) { String action = intent.getAction(); Log.d(TAG, "Received notification, action is: " + action); if (ACTION_UPDATE_POCKET_CODE_VERSION.equals(action)) { final String appPackageName = getPackageName(); StatusBarNotificationManager.getInstance().cancelNotification(intent.getIntExtra("notificationId", 0)); try { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + appPackageName)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); } catch (android.content.ActivityNotFoundException anfe) { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + appPackageName))); } closeNotificationBar(); } if (ACTION_RETRY_UPLOAD.equals(action)) { StatusBarNotificationManager.getInstance().cancelNotification(intent.getBundleExtra("bundle").getInt("notificationId")); Intent reuploadIntent = prepareReuploadIntent(intent); String projectName = intent.getBundleExtra("bundle").getString("projectName"); int notificationId = StatusBarNotificationManager.getInstance().createUploadNotification(getApplicationContext(), projectName); reuploadIntent.putExtra("notificationId", notificationId); getApplicationContext().startService(reuploadIntent); } if (ACTION_CANCEL_UPLOAD.equals(action)) { StatusBarNotificationManager.getInstance().cancelNotification(intent.getBundleExtra("bundle").getInt("notificationId")); closeNotificationBar(); } } private Intent prepareReuploadIntent(Intent intent) { String projectName = intent.getBundleExtra("bundle").getString("projectName"); String projectDescription = intent.getBundleExtra("bundle").getString("projectDescription"); String projectPath = intent.getBundleExtra("bundle").getString("projectPath"); String token = intent.getBundleExtra("bundle").getString("token"); String username = intent.getBundleExtra("bundle").getString("username"); ResultReceiver receiver = intent.getBundleExtra("bundle").getParcelable("receiver"); Intent reuploadIntent = new Intent(getApplicationContext(), ProjectUploadService.class); reuploadIntent.putExtra("receiver", receiver); reuploadIntent.putExtra("uploadName", projectName); reuploadIntent.putExtra("projectDescription", projectDescription); reuploadIntent.putExtra("projectPath", projectPath); reuploadIntent.putExtra("username", username); reuploadIntent.putExtra("token", token); return reuploadIntent; } private void closeNotificationBar() { Intent it = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); sendBroadcast(it); } } }