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 package com.android.wallpaper.picker;
17 
18 import android.Manifest.permission;
19 import android.content.Intent;
20 import android.content.pm.ActivityInfo;
21 import android.content.pm.PackageManager;
22 import android.content.res.Resources;
23 import android.net.Uri;
24 import android.os.Binder;
25 import android.os.Bundle;
26 import android.util.Log;
27 
28 import androidx.annotation.NonNull;
29 import androidx.fragment.app.Fragment;
30 import androidx.fragment.app.FragmentManager;
31 
32 import com.android.wallpaper.R;
33 import com.android.wallpaper.config.BaseFlags;
34 import com.android.wallpaper.model.ImageWallpaperInfo;
35 import com.android.wallpaper.module.InjectorProvider;
36 import com.android.wallpaper.picker.AppbarFragment.AppbarFragmentHost;
37 import com.android.wallpaper.picker.preview.ui.WallpaperPreviewActivity;
38 
39 /**
40  * Activity that displays a preview of a specific wallpaper and provides the ability to set the
41  * wallpaper as the user's current wallpaper. It's "standalone" meaning it doesn't reside in the
42  * app navigation hierarchy and can be launched directly via an explicit intent.
43  */
44 public class StandalonePreviewActivity extends BasePreviewActivity implements AppbarFragmentHost {
45     private static final String TAG = "StandalonePreview";
46     private static final String KEY_UP_ARROW = "up_arrow";
47     private static final int READ_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 1;
48 
49     @Override
onCreate(Bundle savedInstanceState)50     protected void onCreate(Bundle savedInstanceState) {
51         super.onCreate(savedInstanceState);
52         setContentView(R.layout.activity_preview);
53 
54         enableFullScreen();
55 
56         Intent cropAndSetWallpaperIntent = getIntent();
57         Uri imageUri = cropAndSetWallpaperIntent.getData();
58 
59         if (imageUri == null) {
60             Log.e(TAG, "No URI passed in intent; exiting StandalonePreviewActivity");
61             finish();
62             return;
63         }
64 
65         // Check if READ_MEDIA_IMAGES permission is needed because the app invoking this activity
66         // passed a file:// URI or a content:// URI without a flag to grant read permission.
67         boolean isReadPermissionGrantedForImageUri = isReadPermissionGrantedForImageUri(imageUri);
68 
69         // Request storage permission if necessary (i.e., on Android M and later if storage
70         // permission has not already been granted) and delay loading the PreviewFragment until the
71         // permission is granted.
72         if (!isReadPermissionGrantedForImageUri
73                 && !isReadExternalStoragePermissionGrantedForApp()) {
74             requestPermissions(
75                     new String[]{permission.READ_MEDIA_IMAGES},
76                     READ_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
77         }
78     }
79 
80     @Override
onAttachedToWindow()81     public void onAttachedToWindow() {
82         super.onAttachedToWindow();
83 
84         FragmentManager fragmentManager = getSupportFragmentManager();
85         Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_container);
86 
87         if (fragment == null) {
88             loadPreviewFragment();
89         }
90     }
91 
92     @SuppressWarnings("MissingSuperCall") // TODO: Fix me
93     @Override
onResume()94     protected void onResume() {
95         super.onResume();
96         Resources res = getResources();
97         boolean isDeviceFoldableOrTablet = res.getBoolean(R.bool.is_large_screen);
98 
99         if (!isDeviceFoldableOrTablet) {
100             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
101         }
102     }
103 
104     @SuppressWarnings("MissingSuperCall") // TODO: Fix me
105     @Override
onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)106     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
107             @NonNull int[] grantResults) {
108         // Load the preview fragment if the storage permission was granted.
109         if (requestCode == READ_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE) {
110             boolean isGranted = permissions.length > 0
111                     && permissions[0].equals(permission.READ_MEDIA_IMAGES)
112                     && grantResults.length > 0
113                     && grantResults[0] == PackageManager.PERMISSION_GRANTED;
114 
115             // Close the activity because we can't open the image without storage permission.
116             if (!isGranted) {
117                 finish();
118             }
119 
120             loadPreviewFragment();
121         }
122     }
123 
124     // TODO(b/182972395): It should go back to WallpaperPicker.
125     @Override
onUpArrowPressed()126     public void onUpArrowPressed() {
127         // Enable back functions for multi-pane.
128         onBackPressed();
129     }
130 
131     // TODO(b/182972395): It should go back to WallpaperPicker.
132     @Override
isUpArrowSupported()133     public boolean isUpArrowSupported() {
134         // Show up arrow for multi-pane.
135         return getIntent().getBooleanExtra(KEY_UP_ARROW, false);
136     }
137 
138     /**
139      * Creates a new instance of {@link PreviewFragment} and loads the fragment into this
140      * activity's fragment container so that it's shown to the user.
141      */
loadPreviewFragment()142     private void loadPreviewFragment() {
143         BaseFlags flags = InjectorProvider.getInjector().getFlags();
144         Intent intent = getIntent();
145         if (flags.isMultiCropEnabled()) {
146             Intent wallpaperIntent = WallpaperPreviewActivity.Companion.newIntent(
147                     this.getApplicationContext(), intent, /* isAssetIdPresent= */ false,
148                     /* isViewAsHome= */ true, /* isNewTask= */ false);
149             startActivity(wallpaperIntent);
150             finish();
151             return;
152         }
153         Fragment fragment = InjectorProvider.getInjector().getPreviewFragment(
154                 /* context */ this,
155                 new ImageWallpaperInfo(intent.getData()),
156                 /* viewAsHome= */ true,
157                 /* isAssetIdPresent= */ false,
158                 /* isNewTask= */ false);
159         getSupportFragmentManager().beginTransaction()
160                 .add(R.id.fragment_container, fragment)
161                 .commit();
162     }
163 
164     /**
165      * Returns whether the user has granted READ_MEDIA_IMAGES permission to the app.
166      */
isReadExternalStoragePermissionGrantedForApp()167     private boolean isReadExternalStoragePermissionGrantedForApp() {
168         return getPackageManager().checkPermission(permission.READ_MEDIA_IMAGES,
169                 getPackageName()) == PackageManager.PERMISSION_GRANTED;
170     }
171 
172     /**
173      * Returns whether the provided image Uri is readable without requiring the app to have the user
174      * grant READ_MEDIA_IMAGES permission.
175      */
isReadPermissionGrantedForImageUri(Uri imageUri)176     private boolean isReadPermissionGrantedForImageUri(Uri imageUri) {
177         return checkUriPermission(
178                 imageUri,
179                 Binder.getCallingPid(),
180                 Binder.getCallingUid(),
181                 Intent.FLAG_GRANT_READ_URI_PERMISSION) == PackageManager.PERMISSION_GRANTED;
182     }
183 }
184