1 /* 2 * Copyright (C) 2010 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.apps.tag.record; 18 19 import com.android.apps.tag.R; 20 import com.google.common.annotations.VisibleForTesting; 21 import com.google.common.base.Preconditions; 22 import com.google.common.collect.BiMap; 23 import com.google.common.collect.ImmutableBiMap; 24 import com.google.common.primitives.Bytes; 25 26 import android.Manifest; 27 import android.app.Activity; 28 import android.content.ActivityNotFoundException; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.pm.PackageManager; 32 import android.content.pm.ResolveInfo; 33 import android.net.Uri; 34 import android.nfc.NdefRecord; 35 import android.os.Parcel; 36 import android.os.Parcelable; 37 import android.support.v4.app.ActivityCompat; 38 import android.support.v4.content.FileProvider; 39 import android.telephony.PhoneNumberUtils; 40 import android.text.TextUtils; 41 import android.util.Log; 42 import android.view.LayoutInflater; 43 import android.view.View; 44 import android.view.View.OnClickListener; 45 import android.view.ViewGroup; 46 import android.widget.ImageView; 47 import android.widget.TextView; 48 import android.widget.Toast; 49 50 import java.io.File; 51 import java.nio.charset.Charset; 52 import java.util.Arrays; 53 import java.util.List; 54 import java.util.Locale; 55 56 /** 57 * A parsed record containing a Uri. 58 */ 59 public class UriRecord extends ParsedNdefRecord implements OnClickListener { 60 private static final String TAG = "UriRecord"; 61 62 public static final String RECORD_TYPE = "UriRecord"; 63 64 private final Uri mUri; 65 66 private UriRecord(Uri uri) { 67 this.mUri = Preconditions.checkNotNull(uri); 68 } 69 70 public Intent getIntentForUri() { 71 String scheme = mUri.getScheme(); 72 if ("tel".equals(scheme)) { 73 return new Intent(Intent.ACTION_CALL, mUri); 74 } else if ("sms".equals(scheme) || "smsto".equals(scheme)) { 75 return new Intent(Intent.ACTION_SENDTO, mUri); 76 } else { 77 return new Intent(Intent.ACTION_VIEW, mUri); 78 } 79 } 80 81 public String getPrettyUriString(Context context) { 82 String scheme = mUri.getScheme(); 83 boolean tel = "tel".equals(scheme); 84 boolean sms = "sms".equals(scheme) || "smsto".equals(scheme); 85 if (tel || sms) { 86 String ssp = mUri.getSchemeSpecificPart(); 87 int offset = ssp.indexOf('?'); 88 if (offset >= 0) { 89 ssp = ssp.substring(0, offset); 90 } 91 if (tel) { 92 return context.getString(R.string.action_call, PhoneNumberUtils.formatNumber(ssp)); 93 } else { 94 return context.getString(R.string.action_text, PhoneNumberUtils.formatNumber(ssp)); 95 } 96 } else { 97 return mUri.toString(); 98 } 99 } 100 101 @Override 102 public View getView(Activity activity, LayoutInflater inflater, ViewGroup parent, int offset) { 103 return RecordUtils.getViewsForIntent(activity, inflater, parent, this, getIntentForUri(), 104 getPrettyUriString(activity)); 105 } 106 107 @Override 108 public String getSnippet(Context context, Locale locale) { 109 return getPrettyUriString(context); 110 } 111 112 @Override 113 public void onClick(View view) { 114 RecordUtils.ClickInfo info = (RecordUtils.ClickInfo) view.getTag(); 115 if (requestPermissionIfNeeded(info.activity, info.intent)) { 116 return; 117 } 118 try { 119 if (mUri.getScheme().equalsIgnoreCase("file")) { 120 Uri uri = FileProvider.getUriForFile(info.activity, 121 "com.android.apps.tag.provider", new File(mUri.getPath())); 122 Intent intent = new Intent(Intent.ACTION_VIEW, uri); 123 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 124 info.intent = intent; 125 } 126 info.activity.startActivity(info.intent); 127 info.activity.finish(); 128 } catch (ActivityNotFoundException e) { 129 // The activity wansn't found for some reason. Don't crash, but don't do anything. 130 Log.e(TAG, "Failed to launch activity for intent " + info.intent, e); 131 } 132 } 133 134 @VisibleForTesting 135 public Uri getUri() { 136 return mUri; 137 } 138 139 /** 140 * Convert {@link android.nfc.NdefRecord} into a {@link android.net.Uri}. This will handle 141 * both TNF_WELL_KNOWN / RTD_URI and TNF_ABSOLUTE_URI. 142 * 143 * @throws IllegalArgumentException if the NdefRecord is not a 144 * record containing a URI. 145 */ 146 public static UriRecord parse(NdefRecord record) { 147 Uri uri = record.toUri(); 148 if (uri == null) throw new IllegalArgumentException("not a uri"); 149 return new UriRecord(uri); 150 } 151 152 public static boolean isUri(NdefRecord record) { 153 return record.toUri() != null; 154 } 155 156 /** 157 * Convert a {@link Uri} to an {@link NdefRecord} 158 */ 159 public static NdefRecord newUriRecord(Uri uri) { 160 return NdefRecord.createUri(uri); 161 } 162 163 private boolean requestPermissionIfNeeded(Activity activity, Intent intent) { 164 boolean needRequestPermission = false; 165 if (Intent.ACTION_CALL.equals(intent.getAction())) { 166 if (activity.checkSelfPermission(Manifest.permission.CALL_PHONE) 167 != PackageManager.PERMISSION_GRANTED) { 168 /* In case the user selected "Do not ask" for permission again. 169 * Display a message on how to change the permission selection. */ 170 if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CALL_PHONE)) 171 Toast.makeText(activity.getApplicationContext(), R.string.call_phone_permission_denied, Toast.LENGTH_SHORT).show(); 172 needRequestPermission = true; 173 activity.requestPermissions(new String[]{Manifest.permission.CALL_PHONE}, 1); 174 } 175 } 176 177 if (mUri != null && mUri.getScheme().equalsIgnoreCase("file") && 178 activity.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { 179 if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.READ_EXTERNAL_STORAGE)) 180 Toast.makeText(activity.getApplicationContext(), R.string.external_storage_permission_denied, Toast.LENGTH_SHORT).show(); 181 needRequestPermission = true; 182 activity.requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1); 183 } 184 return needRequestPermission; 185 } 186 } 187