1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.providers.downloads; 18 19 import android.app.DownloadManager; 20 import android.content.ActivityNotFoundException; 21 import android.content.BroadcastReceiver; 22 import android.content.ContentUris; 23 import android.content.ContentValues; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.database.Cursor; 27 import android.net.ConnectivityManager; 28 import android.net.NetworkInfo; 29 import android.net.Uri; 30 import android.provider.Downloads; 31 import android.util.Log; 32 33 import com.google.common.annotations.VisibleForTesting; 34 35 import java.io.File; 36 37 /** 38 * Receives system broadcasts (boot, network connectivity) 39 */ 40 public class DownloadReceiver extends BroadcastReceiver { 41 @VisibleForTesting 42 SystemFacade mSystemFacade = null; 43 onReceive(Context context, Intent intent)44 public void onReceive(Context context, Intent intent) { 45 if (mSystemFacade == null) { 46 mSystemFacade = new RealSystemFacade(context); 47 } 48 49 String action = intent.getAction(); 50 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { 51 startService(context); 52 } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { 53 NetworkInfo info = (NetworkInfo) 54 intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); 55 if (info != null && info.isConnected()) { 56 startService(context); 57 } 58 } else if (action.equals(Constants.ACTION_RETRY)) { 59 startService(context); 60 } else if (action.equals(Constants.ACTION_OPEN) 61 || action.equals(Constants.ACTION_LIST) 62 || action.equals(Constants.ACTION_HIDE)) { 63 handleNotificationBroadcast(context, intent); 64 } 65 } 66 67 /** 68 * Handle any broadcast related to a system notification. 69 */ handleNotificationBroadcast(Context context, Intent intent)70 private void handleNotificationBroadcast(Context context, Intent intent) { 71 Uri uri = intent.getData(); 72 String action = intent.getAction(); 73 if (Constants.LOGVV) { 74 if (action.equals(Constants.ACTION_OPEN)) { 75 Log.v(Constants.TAG, "Receiver open for " + uri); 76 } else if (action.equals(Constants.ACTION_LIST)) { 77 Log.v(Constants.TAG, "Receiver list for " + uri); 78 } else { // ACTION_HIDE 79 Log.v(Constants.TAG, "Receiver hide for " + uri); 80 } 81 } 82 83 Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); 84 if (cursor == null) { 85 return; 86 } 87 try { 88 if (!cursor.moveToFirst()) { 89 return; 90 } 91 92 if (action.equals(Constants.ACTION_OPEN)) { 93 openDownload(context, cursor); 94 hideNotification(context, uri, cursor); 95 } else if (action.equals(Constants.ACTION_LIST)) { 96 sendNotificationClickedIntent(intent, cursor); 97 } else { // ACTION_HIDE 98 hideNotification(context, uri, cursor); 99 } 100 } finally { 101 cursor.close(); 102 } 103 } 104 105 /** 106 * Hide a system notification for a download. 107 * @param uri URI to update the download 108 * @param cursor Cursor for reading the download's fields 109 */ hideNotification(Context context, Uri uri, Cursor cursor)110 private void hideNotification(Context context, Uri uri, Cursor cursor) { 111 mSystemFacade.cancelNotification(ContentUris.parseId(uri)); 112 113 int statusColumn = cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_STATUS); 114 int status = cursor.getInt(statusColumn); 115 int visibilityColumn = 116 cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_VISIBILITY); 117 int visibility = cursor.getInt(visibilityColumn); 118 if (Downloads.Impl.isStatusCompleted(status) 119 && visibility == Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) { 120 ContentValues values = new ContentValues(); 121 values.put(Downloads.Impl.COLUMN_VISIBILITY, 122 Downloads.Impl.VISIBILITY_VISIBLE); 123 context.getContentResolver().update(uri, values, null, null); 124 } 125 } 126 127 /** 128 * Open the download that cursor is currently pointing to, since it's completed notification 129 * has been clicked. 130 */ openDownload(Context context, Cursor cursor)131 private void openDownload(Context context, Cursor cursor) { 132 String filename = cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl._DATA)); 133 String mimetype = 134 cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_MIME_TYPE)); 135 Uri path = Uri.parse(filename); 136 // If there is no scheme, then it must be a file 137 if (path.getScheme() == null) { 138 path = Uri.fromFile(new File(filename)); 139 } 140 141 Intent activityIntent = new Intent(Intent.ACTION_VIEW); 142 activityIntent.setDataAndType(path, mimetype); 143 activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 144 try { 145 context.startActivity(activityIntent); 146 } catch (ActivityNotFoundException ex) { 147 Log.d(Constants.TAG, "no activity for " + mimetype, ex); 148 } 149 } 150 151 /** 152 * Notify the owner of a running download that its notification was clicked. 153 * @param intent the broadcast intent sent by the notification manager 154 * @param cursor Cursor for reading the download's fields 155 */ sendNotificationClickedIntent(Intent intent, Cursor cursor)156 private void sendNotificationClickedIntent(Intent intent, Cursor cursor) { 157 String pckg = cursor.getString( 158 cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE)); 159 if (pckg == null) { 160 return; 161 } 162 163 String clazz = cursor.getString( 164 cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_NOTIFICATION_CLASS)); 165 boolean isPublicApi = 166 cursor.getInt(cursor.getColumnIndex(Downloads.Impl.COLUMN_IS_PUBLIC_API)) != 0; 167 168 Intent appIntent = null; 169 if (isPublicApi) { 170 appIntent = new Intent(DownloadManager.ACTION_NOTIFICATION_CLICKED); 171 appIntent.setPackage(pckg); 172 } else { // legacy behavior 173 if (clazz == null) { 174 return; 175 } 176 appIntent = new Intent(Downloads.Impl.ACTION_NOTIFICATION_CLICKED); 177 appIntent.setClassName(pckg, clazz); 178 if (intent.getBooleanExtra("multiple", true)) { 179 appIntent.setData(Downloads.Impl.CONTENT_URI); 180 } else { 181 long downloadId = cursor.getLong(cursor.getColumnIndexOrThrow(Downloads.Impl._ID)); 182 appIntent.setData( 183 ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, downloadId)); 184 } 185 } 186 187 mSystemFacade.sendBroadcast(appIntent); 188 } 189 startService(Context context)190 private void startService(Context context) { 191 context.startService(new Intent(context, DownloadService.class)); 192 } 193 } 194