| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.providers.downloads; |
| |
| import android.app.DownloadManager; |
| import android.content.ActivityNotFoundException; |
| import android.content.BroadcastReceiver; |
| import android.content.ContentUris; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.database.Cursor; |
| import android.net.ConnectivityManager; |
| import android.net.NetworkInfo; |
| import android.net.Uri; |
| import android.provider.Downloads; |
| import android.util.Log; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| |
| import java.io.File; |
| |
| /** |
| * Receives system broadcasts (boot, network connectivity) |
| */ |
| public class DownloadReceiver extends BroadcastReceiver { |
| @VisibleForTesting |
| SystemFacade mSystemFacade = null; |
| |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| if (mSystemFacade == null) { |
| mSystemFacade = new RealSystemFacade(context); |
| } |
| |
| String action = intent.getAction(); |
| if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { |
| if (Constants.LOGVV) { |
| Log.v(Constants.TAG, "Received broadcast intent for " + |
| Intent.ACTION_BOOT_COMPLETED); |
| } |
| startService(context); |
| } else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) { |
| if (Constants.LOGVV) { |
| Log.v(Constants.TAG, "Received broadcast intent for " + |
| Intent.ACTION_MEDIA_MOUNTED); |
| } |
| startService(context); |
| } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { |
| final ConnectivityManager connManager = (ConnectivityManager) context |
| .getSystemService(Context.CONNECTIVITY_SERVICE); |
| final NetworkInfo info = connManager.getActiveNetworkInfo(); |
| if (info != null && info.isConnected()) { |
| startService(context); |
| } |
| } else if (action.equals(Constants.ACTION_RETRY)) { |
| startService(context); |
| } else if (action.equals(Constants.ACTION_OPEN) |
| || action.equals(Constants.ACTION_LIST) |
| || action.equals(Constants.ACTION_HIDE)) { |
| handleNotificationBroadcast(context, intent); |
| } |
| } |
| |
| /** |
| * Handle any broadcast related to a system notification. |
| */ |
| private void handleNotificationBroadcast(Context context, Intent intent) { |
| Uri uri = intent.getData(); |
| String action = intent.getAction(); |
| if (Constants.LOGVV) { |
| if (action.equals(Constants.ACTION_OPEN)) { |
| Log.v(Constants.TAG, "Receiver open for " + uri); |
| } else if (action.equals(Constants.ACTION_LIST)) { |
| Log.v(Constants.TAG, "Receiver list for " + uri); |
| } else { // ACTION_HIDE |
| Log.v(Constants.TAG, "Receiver hide for " + uri); |
| } |
| } |
| |
| Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); |
| if (cursor == null) { |
| return; |
| } |
| try { |
| if (!cursor.moveToFirst()) { |
| return; |
| } |
| |
| if (action.equals(Constants.ACTION_OPEN)) { |
| openDownload(context, cursor); |
| hideNotification(context, uri, cursor); |
| } else if (action.equals(Constants.ACTION_LIST)) { |
| sendNotificationClickedIntent(intent, cursor); |
| } else { // ACTION_HIDE |
| hideNotification(context, uri, cursor); |
| } |
| } finally { |
| cursor.close(); |
| } |
| } |
| |
| /** |
| * Hide a system notification for a download. |
| * @param uri URI to update the download |
| * @param cursor Cursor for reading the download's fields |
| */ |
| private void hideNotification(Context context, Uri uri, Cursor cursor) { |
| mSystemFacade.cancelNotification(ContentUris.parseId(uri)); |
| |
| int statusColumn = cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_STATUS); |
| int status = cursor.getInt(statusColumn); |
| int visibilityColumn = |
| cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_VISIBILITY); |
| int visibility = cursor.getInt(visibilityColumn); |
| if (Downloads.Impl.isStatusCompleted(status) |
| && visibility == Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) { |
| ContentValues values = new ContentValues(); |
| values.put(Downloads.Impl.COLUMN_VISIBILITY, |
| Downloads.Impl.VISIBILITY_VISIBLE); |
| context.getContentResolver().update(uri, values, null, null); |
| } |
| } |
| |
| /** |
| * Open the download that cursor is currently pointing to, since it's completed notification |
| * has been clicked. |
| */ |
| private void openDownload(Context context, Cursor cursor) { |
| String filename = cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl._DATA)); |
| String mimetype = |
| cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_MIME_TYPE)); |
| Uri path = Uri.parse(filename); |
| // If there is no scheme, then it must be a file |
| if (path.getScheme() == null) { |
| path = Uri.fromFile(new File(filename)); |
| } |
| |
| Intent activityIntent = new Intent(Intent.ACTION_VIEW); |
| mimetype = DownloadDrmHelper.getOriginalMimeType(context, filename, mimetype); |
| activityIntent.setDataAndType(path, mimetype); |
| activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| try { |
| context.startActivity(activityIntent); |
| } catch (ActivityNotFoundException ex) { |
| Log.d(Constants.TAG, "no activity for " + mimetype, ex); |
| } |
| } |
| |
| /** |
| * Notify the owner of a running download that its notification was clicked. |
| * @param intent the broadcast intent sent by the notification manager |
| * @param cursor Cursor for reading the download's fields |
| */ |
| private void sendNotificationClickedIntent(Intent intent, Cursor cursor) { |
| String pckg = cursor.getString( |
| cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE)); |
| if (pckg == null) { |
| return; |
| } |
| |
| String clazz = cursor.getString( |
| cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_NOTIFICATION_CLASS)); |
| boolean isPublicApi = |
| cursor.getInt(cursor.getColumnIndex(Downloads.Impl.COLUMN_IS_PUBLIC_API)) != 0; |
| |
| Intent appIntent = null; |
| if (isPublicApi) { |
| appIntent = new Intent(DownloadManager.ACTION_NOTIFICATION_CLICKED); |
| appIntent.setPackage(pckg); |
| // send id of the items clicked on. |
| if (intent.getBooleanExtra("multiple", false)) { |
| // broadcast received saying click occurred on a notification with multiple titles. |
| // don't include any ids at all - let the caller query all downloads belonging to it |
| // TODO modify the broadcast to include ids of those multiple notifications. |
| } else { |
| appIntent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS, |
| new long[] { |
| cursor.getLong(cursor.getColumnIndexOrThrow(Downloads.Impl._ID))}); |
| } |
| } else { // legacy behavior |
| if (clazz == null) { |
| return; |
| } |
| appIntent = new Intent(DownloadManager.ACTION_NOTIFICATION_CLICKED); |
| appIntent.setClassName(pckg, clazz); |
| if (intent.getBooleanExtra("multiple", true)) { |
| appIntent.setData(Downloads.Impl.CONTENT_URI); |
| } else { |
| long downloadId = cursor.getLong(cursor.getColumnIndexOrThrow(Downloads.Impl._ID)); |
| appIntent.setData( |
| ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, downloadId)); |
| } |
| } |
| |
| mSystemFacade.sendBroadcast(appIntent); |
| } |
| |
| private void startService(Context context) { |
| context.startService(new Intent(context, DownloadService.class)); |
| } |
| } |