1/*
2 * Copyright (C) 2011 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 androidx.legacy.app;
18
19import android.app.Fragment;
20import android.content.Context;
21import android.content.pm.PackageManager;
22import android.os.Build;
23import android.os.Handler;
24import android.os.Looper;
25
26import androidx.annotation.NonNull;
27import androidx.annotation.RequiresApi;
28import androidx.annotation.RestrictTo;
29
30import java.util.Arrays;
31
32/**
33 * Helper for accessing features in {@link Fragment} in a backwards compatible fashion.
34 *
35 * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework fragment.
36 */
37@Deprecated
38public class FragmentCompat {
39
40    /**
41     * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework fragment.
42     */
43    @Deprecated
44    public FragmentCompat() {
45    }
46
47    interface FragmentCompatImpl {
48        void setUserVisibleHint(Fragment f, boolean deferStart);
49        void requestPermissions(Fragment fragment, String[] permissions, int requestCode);
50        boolean shouldShowRequestPermissionRationale(Fragment fragment, String permission);
51    }
52
53    /**
54     * Customizable delegate that allows delegating permission related compatibility methods
55     * to a custom implementation.
56     *
57     * <p>
58     *     To delegate fragment compatibility methods to a custom class, implement this interface,
59     *     and call {@code FragmentCompat.setPermissionCompatDelegate(delegate);}. All future calls
60     *     to the compatibility methods in this class will first check whether the delegate can
61     *     handle the method call, and invoke the corresponding method if it can.
62     * </p>
63     *
64     * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework
65     * {@link Fragment}.
66     */
67    @Deprecated
68    public interface PermissionCompatDelegate {
69
70        /**
71         * Determines whether the delegate should handle
72         * {@link FragmentCompat#requestPermissions(Fragment, String[], int)}, and request
73         * permissions if applicable. If this method returns true, it means that permission
74         * request is successfully handled by the delegate, and platform should not perform any
75         * further requests for permission.
76         *
77         * @param fragment The target fragment.
78         * @param permissions The requested permissions.
79         * @param requestCode Application specific request code to match with a result
80         *    reported to {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult(
81         *    int, String[], int[])}.
82         *
83         * @return Whether the delegate has handled the permission request.
84         * @see FragmentCompat#requestPermissions(Fragment, String[], int)
85         *
86         * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework
87         * {@link Fragment}.
88         */
89        @Deprecated
90        boolean requestPermissions(Fragment fragment, String[] permissions, int requestCode);
91    }
92
93    static class FragmentCompatBaseImpl implements FragmentCompatImpl {
94        @Override
95        public void setUserVisibleHint(Fragment f, boolean deferStart) {
96        }
97        @Override
98        public void requestPermissions(final Fragment fragment, final String[] permissions,
99                final int requestCode) {
100            Handler handler = new Handler(Looper.getMainLooper());
101            handler.post(new Runnable() {
102                @Override
103                public void run() {
104                    final int[] grantResults = new int[permissions.length];
105
106                    Context context = fragment.getActivity();
107                    if (context != null) {
108                        PackageManager packageManager = context.getPackageManager();
109                        String packageName = context.getPackageName();
110
111                        final int permissionCount = permissions.length;
112                        for (int i = 0; i < permissionCount; i++) {
113                            grantResults[i] = packageManager.checkPermission(
114                                    permissions[i], packageName);
115                        }
116                    } else {
117                        Arrays.fill(grantResults, PackageManager.PERMISSION_DENIED);
118                    }
119
120                    ((OnRequestPermissionsResultCallback) fragment).onRequestPermissionsResult(
121                            requestCode, permissions, grantResults);
122                }
123            });
124        }
125        @Override
126        public boolean shouldShowRequestPermissionRationale(Fragment fragment, String permission) {
127            return false;
128        }
129    }
130
131    @RequiresApi(15)
132    static class FragmentCompatApi15Impl extends FragmentCompatBaseImpl {
133        @Override
134        public void setUserVisibleHint(Fragment f, boolean deferStart) {
135            f.setUserVisibleHint(deferStart);
136        }
137    }
138
139    @RequiresApi(23)
140    static class FragmentCompatApi23Impl extends FragmentCompatApi15Impl {
141        @Override
142        public void requestPermissions(Fragment fragment, String[] permissions, int requestCode) {
143            fragment.requestPermissions(permissions, requestCode);
144        }
145
146        @Override
147        public boolean shouldShowRequestPermissionRationale(Fragment fragment, String permission) {
148            return fragment.shouldShowRequestPermissionRationale(permission);
149        }
150    }
151
152    @RequiresApi(24)
153    static class FragmentCompatApi24Impl extends FragmentCompatApi23Impl {
154        @Override
155        public void setUserVisibleHint(Fragment f, boolean deferStart) {
156            f.setUserVisibleHint(deferStart);
157        }
158    }
159
160    static final FragmentCompatImpl IMPL;
161    static {
162        if (Build.VERSION.SDK_INT >= 24) {
163            IMPL = new FragmentCompatApi24Impl();
164        } else if (Build.VERSION.SDK_INT >= 23) {
165            IMPL = new FragmentCompatApi23Impl();
166        } else if (android.os.Build.VERSION.SDK_INT >= 15) {
167            IMPL = new FragmentCompatApi15Impl();
168        } else {
169            IMPL = new FragmentCompatBaseImpl();
170        }
171    }
172
173    private static PermissionCompatDelegate sDelegate;
174
175    /**
176     * Sets the permission delegate for {@code FragmentCompat}. Replaces the previously set
177     * delegate.
178     *
179     * @param delegate The delegate to be set. {@code null} to clear the set delegate.
180     *
181     * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework
182     * {@link Fragment}.
183     */
184    @Deprecated
185    public static void setPermissionCompatDelegate(PermissionCompatDelegate delegate) {
186        sDelegate = delegate;
187    }
188
189    /**
190     * @hide
191     *
192     * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework
193     * {@link Fragment}.
194     */
195    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
196    @Deprecated
197    public static PermissionCompatDelegate getPermissionCompatDelegate() {
198        return sDelegate;
199    }
200
201    /**
202     * This interface is the contract for receiving the results for permission requests.
203     *
204     * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework
205     * {@link Fragment}.
206     */
207    @Deprecated
208    public interface OnRequestPermissionsResultCallback {
209
210        /**
211         * Callback for the result from requesting permissions. This method
212         * is invoked for every call on {@link #requestPermissions(android.app.Fragment,
213         * String[], int)}
214         *
215         * @param requestCode The request code passed in {@link #requestPermissions(
216         *     android.app.Fragment, String[], int)}
217         * @param permissions The requested permissions. Never null.
218         * @param grantResults The grant results for the corresponding permissions
219         *     which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
220         *     or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
221         *
222         * @see #requestPermissions(android.app.Fragment, String[], int)
223         *
224         * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework
225         * {@link Fragment}.
226         */
227        @Deprecated
228        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
229                @NonNull int[] grantResults);
230    }
231
232    /**
233     * Call {@link Fragment#setMenuVisibility(boolean) Fragment.setMenuVisibility(boolean)}
234     * if running on an appropriate version of the platform.
235     *
236     * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework
237     * {@link Fragment}.
238     */
239    @Deprecated
240    public static void setMenuVisibility(Fragment f, boolean visible) {
241        f.setMenuVisibility(visible);
242    }
243
244    /**
245     * Call {@link Fragment#setUserVisibleHint(boolean) setUserVisibleHint(boolean)}
246     * if running on an appropriate version of the platform.
247     *
248     * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework
249     * {@link Fragment}.
250     */
251    @Deprecated
252    public static void setUserVisibleHint(Fragment f, boolean deferStart) {
253        IMPL.setUserVisibleHint(f, deferStart);
254    }
255
256    /**
257     * Requests permissions to be granted to this application. These permissions
258     * must be requested in your manifest, they should not be granted to your app,
259     * and they should have protection level {@link android.content.pm.PermissionInfo
260     * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
261     * the platform or a third-party app.
262     * <p>
263     * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
264     * are granted at install time if requested in the manifest. Signature permissions
265     * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at
266     * install time if requested in the manifest and the signature of your app matches
267     * the signature of the app declaring the permissions.
268     * </p>
269     * <p>
270     * If your app does not have the requested permissions the user will be presented
271     * with UI for accepting them. After the user has accepted or rejected the
272     * requested permissions you will receive a callback reporting whether the
273     * permissions were granted or not. Your fragment has to implement {@link
274     * OnRequestPermissionsResultCallback}
275     * and the results of permission requests will be delivered to its
276     * {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult(
277     * int, String[], int[])}.
278     * </p>
279     * <p>
280     * Note that requesting a permission does not guarantee it will be granted and
281     * your app should be able to run without having this permission.
282     * </p>
283     * <p>
284     * This method may start an activity allowing the user to choose which permissions
285     * to grant and which to reject. Hence, you should be prepared that your activity
286     * may be paused and resumed. Further, granting some permissions may require
287     * a restart of you application. In such a case, the system will recreate the
288     * activity stack before delivering the result to your onRequestPermissionsResult(
289     * int, String[], int[]).
290     * </p>
291     * <p>
292     * When checking whether you have a permission you should use {@link
293     * androidx.core.content.ContextCompat#checkSelfPermission(
294     * android.content.Context, String)}.
295     * </p>
296     *
297     * @param fragment The target fragment.
298     * @param permissions The requested permissions.
299     * @param requestCode Application specific request code to match with a result
300     *    reported to {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult(
301     *    int, String[], int[])}.
302     *
303     * @see androidx.core.content.ContextCompat#checkSelfPermission(
304     *     android.content.Context, String)
305     * @see #shouldShowRequestPermissionRationale(android.app.Fragment, String)
306     *
307     * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework
308     * {@link Fragment}.
309     */
310    @Deprecated
311    public static void requestPermissions(@NonNull Fragment fragment,
312            @NonNull String[] permissions, int requestCode) {
313        if (sDelegate != null && sDelegate.requestPermissions(fragment, permissions, requestCode)) {
314            // Delegate has handled the request.
315            return;
316        }
317
318        IMPL.requestPermissions(fragment, permissions, requestCode);
319    }
320
321    /**
322     * Gets whether you should show UI with rationale for requesting a permission.
323     * You should do this only if you do not have the permission and the context in
324     * which the permission is requested does not clearly communicate to the user
325     * what would be the benefit from granting this permission.
326     * <p>
327     * For example, if you write a camera app, requesting the camera permission
328     * would be expected by the user and no rationale for why it is requested is
329     * needed. If however, the app needs location for tagging photos then a non-tech
330     * savvy user may wonder how location is related to taking photos. In this case
331     * you may choose to show UI with rationale of requesting this permission.
332     * </p>
333     *
334     * @param fragment The target fragment.
335     * @param permission A permission your app wants to request.
336     * @return Whether you can show permission rationale UI.
337     *
338     * @see androidx.core.content.ContextCompat#checkSelfPermission(
339     *     android.content.Context, String)
340     * @see #requestPermissions(android.app.Fragment, String[], int)
341     *
342     * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework
343     * {@link Fragment}.
344     */
345    @Deprecated
346    public static boolean shouldShowRequestPermissionRationale(@NonNull Fragment fragment,
347            @NonNull String permission) {
348        return IMPL.shouldShowRequestPermissionRationale(fragment, permission);
349    }
350}
351