1 /* 2 * Copyright (C) 2018 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.media; 18 19 import static com.android.providers.media.MediaProvider.TAG; 20 21 import android.app.Activity; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.content.Intent; 26 import android.content.pm.PackageManager; 27 import android.content.pm.PackageManager.NameNotFoundException; 28 import android.content.res.Resources; 29 import android.database.Cursor; 30 import android.graphics.Bitmap; 31 import android.net.Uri; 32 import android.os.AsyncTask; 33 import android.os.Bundle; 34 import android.provider.MediaStore.MediaColumns; 35 import android.text.TextUtils; 36 import android.util.Log; 37 import android.util.Size; 38 import android.view.WindowManager; 39 import android.widget.FrameLayout; 40 import android.widget.ImageView; 41 import android.widget.ImageView.ScaleType; 42 import android.widget.TextView; 43 44 import com.android.internal.app.AlertActivity; 45 46 import java.io.IOException; 47 48 public class PermissionActivity extends AlertActivity implements DialogInterface.OnClickListener { 49 @Override onCreate(Bundle savedInstanceState)50 public void onCreate(Bundle savedInstanceState) { 51 super.onCreate(savedInstanceState); 52 53 final CharSequence label; 54 final int resId; 55 try { 56 label = getCallingLabel(); 57 resId = getMessageId(); 58 } catch (Exception e) { 59 Log.w(TAG, e); 60 finish(); 61 return; 62 } 63 64 final Resources res = getResources(); 65 final FrameLayout view = new FrameLayout(this); 66 final int padding = res.getDimensionPixelSize(com.android.internal.R.dimen.default_gap); 67 view.setPadding(padding, padding, padding, padding); 68 new AsyncTask<Void, Void, Description>() { 69 @Override 70 protected Description doInBackground(Void... params) { 71 try { 72 return new Description(PermissionActivity.this, getIntent().getData()); 73 } catch (Exception e) { 74 Log.w(TAG, e); 75 finish(); 76 return null; 77 } 78 } 79 80 @Override 81 protected void onPostExecute(Description result) { 82 if (result == null) return; 83 84 if (result.thumbnail != null) { 85 Log.d(TAG, "Found thumbnail " + result.thumbnail.getWidth() + "x" 86 + result.thumbnail.getHeight()); 87 88 final ImageView child = new ImageView(PermissionActivity.this); 89 child.setScaleType(ScaleType.CENTER_INSIDE); 90 child.setImageBitmap(result.thumbnail); 91 child.setContentDescription(result.contentDescription); 92 view.addView(child); 93 } else { 94 Log.d(TAG, "Found description " + result.contentDescription); 95 96 final TextView child = new TextView(PermissionActivity.this); 97 child.setText(result.contentDescription); 98 view.addView(child); 99 } 100 } 101 }.execute(); 102 103 mAlertParams.mMessage = TextUtils.expandTemplate(getText(resId), label); 104 mAlertParams.mPositiveButtonText = getString(R.string.grant_dialog_button_allow); 105 mAlertParams.mPositiveButtonListener = this; 106 mAlertParams.mNegativeButtonText = getString(R.string.grant_dialog_button_deny); 107 mAlertParams.mNegativeButtonListener = this; 108 mAlertParams.mCancelable = false; 109 mAlertParams.mView = view; 110 setupAlert(); 111 112 getWindow().setCloseOnTouchOutside(false); 113 getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 114 getWindow().addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); 115 } 116 117 @Override onClick(DialogInterface dialog, int which)118 public void onClick(DialogInterface dialog, int which) { 119 final Uri uri = getIntent().getData(); 120 switch (which) { 121 case BUTTON_POSITIVE: 122 Log.d(TAG, "User allowed grant for " + uri); 123 grantUriPermission(getCallingPackage(), uri, 124 Intent.FLAG_GRANT_READ_URI_PERMISSION | 125 Intent.FLAG_GRANT_WRITE_URI_PERMISSION | 126 Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); 127 setResult(Activity.RESULT_OK); 128 finish(); 129 break; 130 case BUTTON_NEGATIVE: 131 Log.d(TAG, "User declined grant for " + uri); 132 finish(); 133 break; 134 } 135 } 136 getCallingLabel()137 private CharSequence getCallingLabel() throws NameNotFoundException { 138 final String callingPackage = getCallingPackage(); 139 if (TextUtils.isEmpty(callingPackage)) { 140 throw new NameNotFoundException("Missing calling package"); 141 } 142 143 final PackageManager pm = getPackageManager(); 144 final CharSequence callingLabel = pm 145 .getApplicationLabel(pm.getApplicationInfo(callingPackage, 0)); 146 if (TextUtils.isEmpty(callingLabel)) { 147 throw new NameNotFoundException("Missing calling package"); 148 } 149 150 return callingLabel; 151 } 152 getMessageId()153 private int getMessageId() throws NameNotFoundException { 154 final Uri uri = getIntent().getData(); 155 final String type = uri.getPathSegments().get(1); 156 switch (type) { 157 case "audio": return R.string.permission_audio; 158 case "video": return R.string.permission_video; 159 case "images": return R.string.permission_images; 160 } 161 throw new NameNotFoundException("Unknown media type " + uri); 162 } 163 164 private static class Description { 165 public Bitmap thumbnail; 166 public CharSequence contentDescription; 167 Description(Context context, Uri uri)168 public Description(Context context, Uri uri) { 169 final Resources res = context.getResources(); 170 final ContentResolver resolver = context.getContentResolver(); 171 172 final Size size = new Size(res.getDisplayMetrics().widthPixels, 173 res.getDisplayMetrics().widthPixels); 174 try { 175 thumbnail = resolver.loadThumbnail(uri, size, null); 176 } catch (IOException e) { 177 Log.w(TAG, e); 178 } 179 try (Cursor c = resolver.query(uri, 180 new String[] { MediaColumns.DISPLAY_NAME }, null, null)) { 181 if (c.moveToFirst()) { 182 contentDescription = c.getString(0); 183 } 184 } 185 186 if (TextUtils.isEmpty(contentDescription)) { 187 throw new IllegalStateException(); 188 } 189 } 190 } 191 } 192