1/* 2 * Copyright (C) 2017 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 17package android.support.v4.content.pm; 18 19import static org.junit.Assert.assertEquals; 20import static org.junit.Assert.assertFalse; 21import static org.junit.Assert.assertNotNull; 22import static org.junit.Assert.assertTrue; 23import static org.mockito.Mockito.any; 24import static org.mockito.Mockito.anyInt; 25import static org.mockito.Mockito.doReturn; 26import static org.mockito.Mockito.eq; 27import static org.mockito.Mockito.isNull; 28import static org.mockito.Mockito.mock; 29import static org.mockito.Mockito.reset; 30import static org.mockito.Mockito.spy; 31import static org.mockito.Mockito.times; 32import static org.mockito.Mockito.verify; 33import static org.mockito.Mockito.when; 34 35import android.annotation.TargetApi; 36import android.app.PendingIntent; 37import android.content.BroadcastReceiver; 38import android.content.Context; 39import android.content.Intent; 40import android.content.IntentFilter; 41import android.content.IntentSender; 42import android.content.pm.ActivityInfo; 43import android.content.pm.PackageManager; 44import android.content.pm.ResolveInfo; 45import android.content.pm.ShortcutInfo; 46import android.content.pm.ShortcutManager; 47import android.graphics.Bitmap; 48import android.os.Build; 49import android.support.test.filters.LargeTest; 50import android.support.test.filters.SmallTest; 51import android.support.test.runner.AndroidJUnit4; 52import android.support.v4.BaseInstrumentationTestCase; 53import android.support.v4.app.TestSupportActivity; 54import android.support.v4.graphics.drawable.IconCompat; 55 56import org.junit.Before; 57import org.junit.Test; 58import org.junit.runner.RunWith; 59import org.mockito.ArgumentCaptor; 60 61import java.util.Arrays; 62import java.util.concurrent.CountDownLatch; 63import java.util.concurrent.TimeUnit; 64 65@RunWith(AndroidJUnit4.class) 66public class ShortcutManagerCompatTest extends BaseInstrumentationTestCase<TestSupportActivity> { 67 68 Context mContext; 69 ShortcutInfoCompat mInfoCompat; 70 71 public ShortcutManagerCompatTest() { 72 super(TestSupportActivity.class); 73 } 74 75 @Before 76 public void setup() { 77 mContext = spy(mActivityTestRule.getActivity()); 78 mInfoCompat = new ShortcutInfoCompat.Builder(mContext, "test-id") 79 .setIcon(IconCompat.createWithBitmap(Bitmap.createBitmap( 80 10, 10, Bitmap.Config.ARGB_8888))) 81 .setShortLabel("Test shortcut") 82 .setIntent(new Intent("Dummy")) 83 .build(); 84 } 85 86 @Test 87 @SmallTest 88 @TargetApi(26) 89 public void testIsRequestPinShortcutSupported_v26() throws Throwable { 90 if (!(Build.VERSION.SDK_INT >= 26)) { 91 return; 92 } 93 94 ShortcutManager mockShortcutManager = mock(ShortcutManager.class); 95 doReturn(mockShortcutManager).when(mContext).getSystemService(eq(Context.SHORTCUT_SERVICE)); 96 when(mockShortcutManager.isRequestPinShortcutSupported()).thenReturn(true, false, true); 97 98 assertTrue(ShortcutManagerCompat.isRequestPinShortcutSupported(mContext)); 99 assertFalse(ShortcutManagerCompat.isRequestPinShortcutSupported(mContext)); 100 assertTrue(ShortcutManagerCompat.isRequestPinShortcutSupported(mContext)); 101 verify(mockShortcutManager, times(3)).isRequestPinShortcutSupported(); 102 } 103 104 @Test 105 @SmallTest 106 @TargetApi(26) 107 public void testRequestPinShortcut_v26() throws Throwable { 108 if (!(Build.VERSION.SDK_INT >= 26)) { 109 return; 110 } 111 112 ShortcutManager mockShortcutManager = mock(ShortcutManager.class); 113 doReturn(mockShortcutManager).when(mContext).getSystemService(eq(Context.SHORTCUT_SERVICE)); 114 when(mockShortcutManager.requestPinShortcut( 115 any(ShortcutInfo.class), any(IntentSender.class))).thenReturn(true); 116 117 assertTrue(ShortcutManagerCompat.requestPinShortcut(mContext, mInfoCompat, null)); 118 ArgumentCaptor<ShortcutInfo> captor = ArgumentCaptor.forClass(ShortcutInfo.class); 119 verify(mockShortcutManager, times(1)).requestPinShortcut(captor.capture(), 120 (IntentSender) isNull()); 121 assertEquals("test-id", captor.getValue().getId()); 122 } 123 124 @Test 125 @SmallTest 126 @TargetApi(26) 127 public void testCreateShortcutResultIntent_v26() throws Throwable { 128 if (!(Build.VERSION.SDK_INT >= 26)) { 129 return; 130 } 131 132 ShortcutManager mockShortcutManager = mock(ShortcutManager.class); 133 doReturn(mockShortcutManager).when(mContext).getSystemService(eq(Context.SHORTCUT_SERVICE)); 134 135 when(mockShortcutManager.createShortcutResultIntent(any(ShortcutInfo.class))) 136 .thenReturn(new Intent("some-dummy-action")); 137 138 Intent result = ShortcutManagerCompat.createShortcutResultIntent(mContext, mInfoCompat); 139 verifyLegacyIntent(result); 140 assertEquals("some-dummy-action", result.getAction()); 141 142 ArgumentCaptor<ShortcutInfo> captor = ArgumentCaptor.forClass(ShortcutInfo.class); 143 verify(mockShortcutManager, times(1)).createShortcutResultIntent(captor.capture()); 144 assertEquals("test-id", captor.getValue().getId()); 145 } 146 147 @SmallTest 148 @Test 149 public void testIsRequestPinShortcutSupported_v4() throws Throwable { 150 if (Build.VERSION.SDK_INT >= 26) { 151 return; 152 } 153 setMockPm(mockResolveInfo(null)); 154 assertTrue(ShortcutManagerCompat.isRequestPinShortcutSupported(mContext)); 155 156 // We do not have the permission 157 setMockPm(mockResolveInfo("com.android.permission.something-we-dont-have")); 158 assertFalse(ShortcutManagerCompat.isRequestPinShortcutSupported(mContext)); 159 160 // There are no receivers 161 setMockPm(); 162 assertFalse(ShortcutManagerCompat.isRequestPinShortcutSupported(mContext)); 163 164 // At least one receiver is supported 165 setMockPm(mockResolveInfo("com.android.permission.something-we-dont-have"), 166 mockResolveInfo(null)); 167 assertTrue(ShortcutManagerCompat.isRequestPinShortcutSupported(mContext)); 168 169 // We have the permission 170 setMockPm(mockResolveInfo(ShortcutManagerCompat.INSTALL_SHORTCUT_PERMISSION)); 171 assertTrue(ShortcutManagerCompat.isRequestPinShortcutSupported(mContext)); 172 } 173 174 @LargeTest 175 @Test 176 public void testRequestPinShortcut_v4_noCallback() throws Throwable { 177 if (Build.VERSION.SDK_INT >= 26) { 178 return; 179 } 180 181 setMockPm(mockResolveInfo(null)); 182 183 BlockingBroadcastReceiver receiver = 184 new BlockingBroadcastReceiver(ShortcutManagerCompat.ACTION_INSTALL_SHORTCUT); 185 assertTrue(ShortcutManagerCompat.requestPinShortcut(mContext, mInfoCompat, null)); 186 verifyLegacyIntent(receiver.blockingGetIntent()); 187 } 188 189 @LargeTest 190 @Test 191 public void testRequestPinShortcut_v4_withCallback() throws Throwable { 192 if (Build.VERSION.SDK_INT >= 26) { 193 return; 194 } 195 196 setMockPm(mockResolveInfo(null)); 197 198 BlockingBroadcastReceiver receiver = 199 new BlockingBroadcastReceiver(ShortcutManagerCompat.ACTION_INSTALL_SHORTCUT); 200 BlockingBroadcastReceiver callback = 201 new BlockingBroadcastReceiver("shortcut-callback"); 202 203 assertTrue(ShortcutManagerCompat.requestPinShortcut(mContext, mInfoCompat, 204 PendingIntent.getBroadcast(mContext, 0, new Intent("shortcut-callback"), 205 PendingIntent.FLAG_ONE_SHOT).getIntentSender())); 206 verifyLegacyIntent(receiver.blockingGetIntent()); 207 assertNotNull(callback.blockingGetIntent()); 208 } 209 210 @SmallTest 211 @Test 212 public void testCreateShortcutResultIntent_v4() throws Throwable { 213 if (Build.VERSION.SDK_INT >= 26) { 214 return; 215 } 216 217 verifyLegacyIntent(ShortcutManagerCompat.createShortcutResultIntent(mContext, mInfoCompat)); 218 } 219 220 private void verifyLegacyIntent(Intent intent) { 221 assertNotNull(intent); 222 assertEquals("Test shortcut", intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME)); 223 assertEquals("Dummy", ((Intent) intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT)) 224 .getAction()); 225 } 226 227 private void setMockPm(ResolveInfo... infos) { 228 PackageManager pm = mock(PackageManager.class); 229 when(pm.queryBroadcastReceivers(any(Intent.class), anyInt())) 230 .thenReturn(Arrays.asList(infos)); 231 reset(mContext); 232 doReturn(pm).when(mContext).getPackageManager(); 233 } 234 235 private ResolveInfo mockResolveInfo(String permission) { 236 ActivityInfo aInfo = new ActivityInfo(); 237 aInfo.packageName = mContext.getPackageName(); 238 aInfo.permission = permission; 239 ResolveInfo rInfo = new ResolveInfo(); 240 rInfo.activityInfo = aInfo; 241 return rInfo; 242 } 243 244 private class BlockingBroadcastReceiver extends BroadcastReceiver { 245 246 private final CountDownLatch mLatch = new CountDownLatch(1); 247 private Intent mIntent; 248 249 BlockingBroadcastReceiver(String action) { 250 mContext.registerReceiver(this, new IntentFilter(action)); 251 } 252 253 @Override 254 public void onReceive(Context context, Intent intent) { 255 mIntent = intent; 256 mLatch.countDown(); 257 } 258 259 public Intent blockingGetIntent() throws InterruptedException { 260 mLatch.await(5, TimeUnit.SECONDS); 261 mContext.unregisterReceiver(this); 262 return mIntent; 263 } 264 } 265} 266