├── .gitignore
├── assets
└── xposed_init
├── res
├── drawable-nodpi
│ ├── ab.png
│ └── emoji_u263a.png
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── drawable-xhdpi
│ └── ic_launcher.png
├── drawable-xxhdpi
│ └── ic_launcher.png
├── drawable-xxxhdpi
│ └── ic_launcher.png
├── values-de
│ └── strings.xml
└── values
│ └── strings.xml
├── src
└── com
│ └── germainz
│ └── playpermissionsexposed
│ ├── PermissionData.java
│ └── XposedMod.java
├── AndroidManifest.xml
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | bin/
3 | gen/
4 | out/
5 | *.apk
6 | *.iml
7 |
--------------------------------------------------------------------------------
/assets/xposed_init:
--------------------------------------------------------------------------------
1 | com.germainz.playpermissionsexposed.XposedMod
2 |
--------------------------------------------------------------------------------
/res/drawable-nodpi/ab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ns130291/PlayPermissionsExposed/master/res/drawable-nodpi/ab.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ns130291/PlayPermissionsExposed/master/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ns130291/PlayPermissionsExposed/master/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-nodpi/emoji_u263a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ns130291/PlayPermissionsExposed/master/res/drawable-nodpi/emoji_u263a.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ns130291/PlayPermissionsExposed/master/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ns130291/PlayPermissionsExposed/master/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ns130291/PlayPermissionsExposed/master/res/drawable-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/values-de/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | %1$d Berechtigungen. Antippen für Details.
4 | diese App
5 | Keine Beschreibung vorhanden.
6 |
7 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | PlayPermissionsExposed
4 | Shows all permissions in the Play Store, stops hiding/ignoring ~40 permissions,
5 | and disables auto-updating when new permissions are added.
6 |
7 | %1$d permissions. Tap for details.
8 | This application
9 | No description available.
10 |
11 |
--------------------------------------------------------------------------------
/src/com/germainz/playpermissionsexposed/PermissionData.java:
--------------------------------------------------------------------------------
1 | package com.germainz.playpermissionsexposed;
2 |
3 | public class PermissionData {
4 | public String permission;
5 | public String name;
6 | public CharSequence shortDescription;
7 | public CharSequence longDescription;
8 |
9 | public PermissionData(String permission, CharSequence name, CharSequence shortDescription, CharSequence longDescription) {
10 | this.permission = permission;
11 | this.name = ((String) name).substring(0, 1).toUpperCase() + ((String) name).substring(1);
12 | this.shortDescription = shortDescription;
13 | this.longDescription = longDescription;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
9 |
10 |
13 |
14 |
17 |
20 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Introduction
2 | ============
3 | The newer Play Store versions bring the following changes in regards to permissions:
4 | * Permissions are now shown under vague categories when you press the "Update"/"Install" buttons. 42 permissions are also hidden from this screen (list below). To see almost all individual permissions, you can scroll down and tap "View details". Also note that unknown permissions (those defined by apps) are usually hidden in both screens.
5 | * Auto-updates only check for new categories, not new permissions.
6 | For more info, I'd recommend you read [this](http://www.xda-developers.com/android/play-store-permissions-change-opens-door-to-rogue-apps/") article by pulser_g2.
7 |
8 | Purpose of this module
9 | ======================
10 | This module aims to fix this problem for users who care about permissions. It'll do the following:
11 | * Make the Play Store show you all of the app's permissions.
12 | * Require you to manually update apps with new permissions (regardless of the category).
13 | It more or less restores the old behavior.
14 |
15 | Download
16 | ========
17 | http://repo.xposed.info/module/com.germainz.playpermissionsexposed
18 |
19 | XDA Thread
20 | ==========
21 | http://forum.xda-developers.com/xposed/modules/playpermissionsexposed-fix-play-store-t2783076
22 |
--------------------------------------------------------------------------------
/src/com/germainz/playpermissionsexposed/XposedMod.java:
--------------------------------------------------------------------------------
1 | package com.germainz.playpermissionsexposed;
2 |
3 | import android.app.AndroidAppHelper;
4 | import android.content.Context;
5 | import android.content.pm.PackageManager;
6 | import android.content.pm.PermissionInfo;
7 | import android.content.res.Resources;
8 | import android.text.Html;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.widget.ImageView;
13 | import android.widget.LinearLayout;
14 | import android.widget.TextView;
15 |
16 | import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
17 | import static de.robv.android.xposed.XposedHelpers.getAdditionalInstanceField;
18 | import static de.robv.android.xposed.XposedHelpers.getObjectField;
19 | import static de.robv.android.xposed.XposedHelpers.setAdditionalInstanceField;
20 | import static de.robv.android.xposed.XposedHelpers.setBooleanField;
21 |
22 | import java.util.ArrayList;
23 | import java.util.Arrays;
24 | import java.util.HashMap;
25 | import java.util.List;
26 | import java.util.Set;
27 |
28 | import de.robv.android.xposed.IXposedHookLoadPackage;
29 | import de.robv.android.xposed.XC_MethodHook;
30 | import de.robv.android.xposed.XC_MethodReplacement;
31 | import de.robv.android.xposed.callbacks.XC_LoadPackage;
32 |
33 | public class XposedMod implements IXposedHookLoadPackage {
34 | protected static final HashMap> PERMISSION_BUCKETS = new HashMap>();
35 |
36 | static {
37 | PERMISSION_BUCKETS.put("ic_perm_in_app_purchases", Arrays.asList(
38 | "com.android.vending.BILLING"
39 | ));
40 | PERMISSION_BUCKETS.put("ic_perm_camera_microphone", Arrays.asList(
41 | "android.permission.CAMERA", "android.permission.RECORD_AUDIO",
42 | "android.permission.RECORD_VIDEO"
43 | ));
44 | PERMISSION_BUCKETS.put("ic_perm_cal", Arrays.asList(
45 | "android.permission.READ_CONTACTS", "android.permission.WRITE_CONTACTS",
46 | "android.permission.READ_CALENDAR", "android.permission.WRITE_CALENDAR"
47 | ));
48 | PERMISSION_BUCKETS.put("ic_perm_history", Arrays.asList(
49 | "android.permission.READ_LOGS", "android.permission.GET_TASKS", "android.permission.DUMP",
50 | "com.android.browser.permission.READ_HISTORY_BOOKMARKS"
51 | ));
52 | PERMISSION_BUCKETS.put("ic_perm_deviceid", Arrays.asList(
53 | "android.permission.READ_PHONE_STATE"
54 | ));
55 | PERMISSION_BUCKETS.put("ic_perm_identity", Arrays.asList(
56 | "android.permission.GET_ACCOUNTS", "android.permission.MANAGE_ACCOUNTS",
57 | "android.permission.READ_PROFILE", "android.permission.WRITE_PROFILE"
58 | ));
59 | PERMISSION_BUCKETS.put("ic_perm_unknown", Arrays.asList(
60 | "android.permission.ACCESS_MOCK_LOCATION", "android.permission.ACCESS_NETWORK_STATE",
61 | "android.permission.ACCOUNT_MANAGER", "android.permission.AUTHENTICATE_ACCOUNTS",
62 | "android.permission.BATTERY_STATS", "android.permission.BLUETOOTH",
63 | "android.permission.BLUETOOTH_ADMIN", "android.permission.BROADCAST_STICKY",
64 | "android.permission.CHANGE_CONFIGURATION", "android.permission.CHANGE_NETWORK_STATE",
65 | "android.permission.CHANGE_WIFI_MULTICAST_STATE", "android.permission.CHANGE_WIFI_STATE",
66 | "android.permission.CHANGE_WIMAX_STATE", "android.permission.CLEAR_APP_CACHE",
67 | "android.permission.DISABLE_KEYGUARD", "android.permission.EXPAND_STATUS_BAR",
68 | "android.permission.FLASHLIGHT", "android.permission.GET_PACKAGE_SIZE", "android.permission.INTERNET",
69 | "android.permission.KILL_BACKGROUND_PROCESSES", "android.permission.MODIFY_AUDIO_SETTINGS",
70 | "android.permission.NFC", "android.permission.PERSISTENT_ACTIVITY",
71 | "android.permission.READ_SYNC_SETTINGS", "android.permission.READ_USER_DICTIONARY",
72 | "android.permission.RECEIVE_BOOT_COMPLETED", "android.permission.REORDER_TASKS",
73 | "android.permission.SERIAL_PORT", "android.permission.SET_ALWAYS_FINISH",
74 | "android.permission.SET_ANIMATION_SCALE", "android.permission.SET_DEBUG_APP",
75 | "android.permission.SET_PREFERRED_APPLICATIONS", "android.permission.SET_PROCESS_LIMIT",
76 | "android.permission.SET_TIME_ZONE", "android.permission.SET_WALLPAPER",
77 | "android.permission.SIGNAL_PERSISTENT_PROCESSES", "android.permission.SYSTEM_ALERT_WINDOW",
78 | "android.permission.USE_CREDENTIALS", "android.permission.USE_SIP", "android.permission.VIBRATE",
79 | "android.permission.WAKE_LOCK", "android.permission.WRITE_SETTINGS",
80 | "android.permission.WRITE_SYNC_SETTINGS", "android.permission.WRITE_USER_DICTIONARY",
81 | "com.android.alarm.permission.SET_ALARM", "com.android.browser.permission.WRITE_HISTORY_BOOKMARKS",
82 | "com.android.launcher.permission.INSTALL_SHORTCUT",
83 | "com.android.launcher.permission.UNINSTALL_SHORTCUT", "com.android.vending.CHECK_LICENSE",
84 | "com.google.android.providers.gsf.permission.READ_GSERVICES"
85 | ));
86 | PERMISSION_BUCKETS.put("ic_perm_location", Arrays.asList(
87 | "android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION",
88 | "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS", "android.permission.ACCESS_GPS"
89 | ));
90 | PERMISSION_BUCKETS.put("ic_perm_phone", Arrays.asList(
91 | "android.permission.CALL_PHONE", "android.permission.WRITE_CALL_LOG",
92 | "android.permission.READ_CALL_LOG", "android.permission.CALL_PRIVILEGED",
93 | "android.permission.PROCESS_OUTGOING_CALLS", "android.permission.MODIFY_PHONE_STATE"
94 | ));
95 | PERMISSION_BUCKETS.put("ic_perm_media", Arrays.asList(
96 | "android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE",
97 | "android.permission.MOUNT_FORMAT_FILESYSTEMS", "android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
98 | ));
99 | PERMISSION_BUCKETS.put("ic_perm_data_settings", Arrays.asList(
100 | "android.permission.WRITE_APN_SETTINGS"
101 | ));
102 | PERMISSION_BUCKETS.put("ic_perm_messaging", Arrays.asList(
103 | "android.permission.RECEIVE_SMS", "android.permission.READ_SMS", "android.permission.WRITE_SMS",
104 | "android.permission.SEND_SMS", "android.permission.RECEIVE_MMS",
105 | "android.permission.RECEIVE_WAP_PUSH"
106 | ));
107 | PERMISSION_BUCKETS.put("ic_perm_scan_wifi", Arrays.asList(
108 | "android.permission.ACCESS_WIFI_STATE"
109 | ));
110 | }
111 |
112 | @Override
113 | public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
114 | if (!lpparam.packageName.equals("com.android.vending"))
115 | return;
116 |
117 | findAndHookMethod("com.google.android.finsky.utils.PermissionsBucketer", lpparam.classLoader,
118 | "getPermissionBuckets",
119 | String[].class, Set.class, boolean.class, boolean.class,
120 | new XC_MethodHook() {
121 | @Override
122 | protected void afterHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
123 | String[] np = (String[]) param.args[0];
124 | List newPermissions;
125 | if (np != null)
126 | newPermissions = new ArrayList(Arrays.asList(np));
127 | else
128 | newPermissions = new ArrayList();
129 |
130 | Set op = (Set) param.args[1];
131 | List oldPermissions;
132 | if (op != null)
133 | oldPermissions = new ArrayList(op);
134 | else
135 | oldPermissions = new ArrayList();
136 |
137 | newPermissions.removeAll(oldPermissions);
138 |
139 | boolean hasnewPermissions = (newPermissions.size() > 0);
140 |
141 | Object permissionData = param.getResult();
142 | setBooleanField(permissionData, "mForcePermissionPrompt", hasnewPermissions);
143 |
144 | Context context = AndroidAppHelper.currentApplication();
145 | PackageManager packageManager = context.getPackageManager();
146 | ArrayList newPermissionsData = new ArrayList();
147 | ArrayList oldPermissionsData = new ArrayList();
148 | for (String permission : newPermissions) {
149 | CharSequence longDescription;
150 | CharSequence name;
151 | try {
152 | PermissionInfo permissionInfo = packageManager.getPermissionInfo(permission,
153 | PackageManager.GET_META_DATA);
154 | longDescription = permissionInfo.loadDescription(packageManager);
155 | name = permissionInfo.loadLabel(packageManager);
156 | } catch (PackageManager.NameNotFoundException ignored) {
157 | longDescription = null;
158 | name = permission;
159 | }
160 | CharSequence shortDescription = null;
161 | newPermissionsData.add(new PermissionData(permission, name, shortDescription,
162 | longDescription));
163 | }
164 | for (String permission : oldPermissions) {
165 | CharSequence name;
166 | CharSequence longDescription;
167 | try {
168 | PermissionInfo permissionInfo = packageManager.getPermissionInfo(permission,
169 | PackageManager.GET_META_DATA);
170 | longDescription = permissionInfo.loadDescription(packageManager);
171 | name = permissionInfo.loadLabel(packageManager);
172 | } catch (PackageManager.NameNotFoundException e) {
173 | longDescription = null;
174 | name = permission;
175 | }
176 | CharSequence shortDescription = null;
177 | oldPermissionsData.add(new PermissionData(permission, name, shortDescription,
178 | longDescription));
179 | }
180 |
181 | setAdditionalInstanceField(permissionData, "newPermissionsData", newPermissionsData);
182 | setAdditionalInstanceField(permissionData, "oldPermissionsData", oldPermissionsData);
183 | setAdditionalInstanceField(permissionData, "adapterSize",
184 | ((newPermissionsData.size() == 0) ? 1 : newPermissions.size()) + (
185 | (oldPermissionsData.size() == 0) ? 0 : 1)
186 | );
187 | param.setResult(permissionData);
188 | }
189 | }
190 | );
191 |
192 | findAndHookMethod("com.google.android.finsky.layout.AppPermissionAdapter", lpparam.classLoader,
193 | "showTheNoPermissionMessage", XC_MethodReplacement.returnConstant(false));
194 |
195 | findAndHookMethod("com.google.android.finsky.layout.AppPermissionAdapter", lpparam.classLoader,
196 | "getCount", new XC_MethodReplacement() {
197 | @Override
198 | protected Object replaceHookedMethod(final MethodHookParam param) throws Throwable {
199 | Object mData = getObjectField(param.thisObject, "mData");
200 | int count = (Integer) getAdditionalInstanceField(mData, "adapterSize");
201 | return count;
202 | }
203 | }
204 | );
205 |
206 | findAndHookMethod("com.google.android.finsky.layout.AppPermissionAdapter", lpparam.classLoader,
207 | "getView", int.class, View.class, ViewGroup.class, new XC_MethodReplacement() {
208 | @Override
209 | protected Object replaceHookedMethod(final MethodHookParam param) throws Throwable {
210 | Object mData = getObjectField(param.thisObject, "mData");
211 | ViewGroup viewGroup = (ViewGroup) param.args[2];
212 | Context context = (Context) getObjectField(param.thisObject, "mContext");
213 | LayoutInflater layoutInflater = (LayoutInflater) getObjectField(param.thisObject,
214 | "mLayoutInflater");
215 | Resources res = context.getResources();
216 |
217 | ArrayList newPermissionsData = (ArrayList)
218 | getAdditionalInstanceField(mData, "newPermissionsData");
219 | ArrayList oldPermissionsData = (ArrayList)
220 | getAdditionalInstanceField(mData, "oldPermissionsData");
221 |
222 | int index = (Integer) param.args[0];
223 | if (index < newPermissionsData.size()) {
224 | return getPermissionView(layoutInflater, viewGroup, res, newPermissionsData.get(index));
225 | } else {
226 | if (index == 0) {
227 | TextView textView = (TextView) layoutInflater.inflate(
228 | getLayoutRes("no_permissions_required", res), viewGroup, false);
229 | textView.setText(Html.fromHtml(res.getString(getStringRes("no_new_permissions", res),
230 | getOwnString(context, R.string.this_application))));
231 | return textView;
232 | } else {
233 | return getExistingPermissionView(layoutInflater, viewGroup, res, oldPermissionsData);
234 | }
235 | }
236 | }
237 | }
238 | );
239 |
240 | }
241 |
242 | private View getPermissionView(LayoutInflater layoutInflater, ViewGroup viewGroup,
243 | final Resources res, PermissionData permissionData) {
244 | View view = layoutInflater.inflate(getLayoutRes("permission_row", res), viewGroup,
245 | false);
246 | TextView headerTextView = (TextView) view.findViewById(getIdRes("header", res));
247 | final TextView contentTextView = (TextView) view.findViewById(getIdRes("content", res));
248 | final ImageView expanderIcon = (ImageView) view.findViewById(getIdRes("expander_icon", res));
249 | ImageView bucketIcon = (ImageView) view.findViewById(getIdRes("bucket_icon", res));
250 |
251 | headerTextView.setText(permissionData.name);
252 | contentTextView.setText(permissionData.longDescription == null ?
253 | getOwnString(AndroidAppHelper.currentApplication(), R.string.no_description) :
254 | permissionData.longDescription);
255 | contentTextView.setVisibility(View.GONE);
256 | bucketIcon.setImageResource(getBucketIconRes(permissionData.permission, res));
257 |
258 | view.setOnClickListener(new View.OnClickListener() {
259 | @Override
260 | public void onClick(View view) {
261 | boolean expanded = (contentTextView.getVisibility() == View.GONE);
262 | if (expanded) {
263 | expanderIcon.setImageResource(getDrawableRes("ic_more_arrow_up", res));
264 | contentTextView.setVisibility(View.VISIBLE);
265 | } else {
266 | expanderIcon.setImageResource(getDrawableRes("ic_more_arrow_down", res));
267 | contentTextView.setVisibility(View.GONE);
268 | }
269 | }
270 | });
271 | return view;
272 | }
273 |
274 | private View getExistingPermissionView(LayoutInflater layoutInflater, ViewGroup viewGroup,
275 | final Resources res, ArrayList permissionsData) {
276 | LinearLayout view = (LinearLayout) layoutInflater.inflate(getLayoutRes("existing_permissions_row", res),
277 | viewGroup, false);
278 | ImageView bucketIcon = (ImageView) view.findViewById(getIdRes("bucket_icon", res));
279 | TextView headerText = (TextView) view.findViewById(getIdRes("header", res));
280 | final ImageView expanderIcon = (ImageView) view.findViewById(getIdRes("expander_icon", res));
281 | final TextView shortDescription = (TextView) view.findViewById(getIdRes("short_description", res));
282 | final LinearLayout detailedBuckets = (LinearLayout) view.findViewById(getIdRes("detailed_buckets", res));
283 | final LinearLayout permissionRow = (LinearLayout) view.findViewById(getIdRes("permission_row", res));
284 |
285 | bucketIcon.setImageResource(getDrawableRes("ic_perm_check", res));
286 | headerText.setText(getStringRes("already_has_access_to", res));
287 | shortDescription.setText(getOwnString(AndroidAppHelper.currentApplication(), R.string.existing_permissions,
288 | permissionsData.size()));
289 | permissionRow.setTag(false);
290 |
291 | view.setOnClickListener(new View.OnClickListener() {
292 | @Override
293 | public void onClick(View view) {
294 | boolean expanded = (Boolean) permissionRow.getTag();
295 | if (!expanded) {
296 | expanderIcon.setImageResource(getDrawableRes("ic_more_arrow_up", res));
297 | detailedBuckets.setVisibility(View.VISIBLE);
298 | shortDescription.setVisibility(View.GONE);
299 | } else {
300 | expanderIcon.setImageResource(getDrawableRes("ic_more_arrow_down", res));
301 | detailedBuckets.setVisibility(View.GONE);
302 | shortDescription.setVisibility(View.VISIBLE);
303 | }
304 | permissionRow.setTag(!expanded);
305 | }
306 | });
307 |
308 | for (PermissionData permissionData : permissionsData) {
309 | View permissionView = getPermissionView(layoutInflater, viewGroup, res, permissionData);
310 | permissionView.setOnClickListener(null);
311 | permissionView.setClickable(false);
312 | permissionView.findViewById(getIdRes("expander_icon", res)).setVisibility(View.GONE);
313 | permissionView.findViewById(getIdRes("content", res)).setVisibility(View.VISIBLE);
314 | detailedBuckets.addView(permissionView);
315 | }
316 |
317 | return view;
318 | }
319 |
320 | private int getResource(String name, String type, Resources res) {
321 | return res.getIdentifier(name, type, "com.android.vending");
322 | }
323 |
324 | private int getIdRes(String name, Resources res) {
325 | return getResource(name, "id", res);
326 | }
327 |
328 | private int getDrawableRes(String name, Resources res) {
329 | return getResource(name, "drawable", res);
330 | }
331 |
332 | private int getStringRes(String name, Resources res) {
333 | return getResource(name, "string", res);
334 | }
335 |
336 | private int getLayoutRes(String name, Resources res) {
337 | return getResource(name, "layout", res);
338 | }
339 |
340 | private int getBucketIconRes(String permission, Resources res) {
341 | for (HashMap.Entry> entry : PERMISSION_BUCKETS.entrySet()) {
342 | String icon = entry.getKey();
343 | List permissions = entry.getValue();
344 | if (permissions.contains(permission))
345 | return getDrawableRes(icon, res);
346 | }
347 | return getDrawableRes("ic_perm_unknown", res);
348 | }
349 |
350 | private static String getOwnString(Context context, int resId, Object... formatArgs) {
351 | Context packageContext;
352 | try {
353 | packageContext = context.createPackageContext("com.germainz.playpermissionsexposed",
354 | Context.CONTEXT_IGNORE_SECURITY);
355 | } catch (PackageManager.NameNotFoundException e) {
356 | return null;
357 | }
358 | if (formatArgs.length > 0)
359 | return packageContext.getString(resId, formatArgs);
360 | else
361 | return packageContext.getString(resId);
362 | }
363 | }
--------------------------------------------------------------------------------