1 /*
2  * Copyright (C) 2015 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.messaging.ui.photoviewer;
18 
19 import android.Manifest;
20 import android.app.Activity;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.database.Cursor;
24 import android.net.Uri;
25 import android.os.Bundle;
26 import androidx.fragment.app.FragmentManager;
27 import androidx.loader.content.Loader;
28 import android.text.TextUtils;
29 import android.view.Menu;
30 import android.view.MenuItem;
31 import android.widget.ShareActionProvider;
32 import android.widget.Toast;
33 
34 import com.android.ex.photo.PhotoViewController;
35 import com.android.ex.photo.adapters.PhotoPagerAdapter;
36 import com.android.ex.photo.loaders.PhotoBitmapLoaderInterface.BitmapResult;
37 import com.android.messaging.R;
38 import com.android.messaging.datamodel.ConversationImagePartsView.PhotoViewQuery;
39 import com.android.messaging.datamodel.MediaScratchFileProvider;
40 import com.android.messaging.ui.conversation.ConversationFragment;
41 import com.android.messaging.util.Dates;
42 import com.android.messaging.util.LogUtil;
43 import com.android.messaging.util.OsUtil;
44 
45 /**
46  * Customizations for the photoviewer to display conversation images in full screen.
47  */
48 public class BuglePhotoViewController extends PhotoViewController {
49     private ShareActionProvider mShareActionProvider;
50     private MenuItem mShareItem;
51     private MenuItem mSaveItem;
52 
BuglePhotoViewController(final ActivityInterface activity)53     public BuglePhotoViewController(final ActivityInterface activity) {
54         super(activity);
55     }
56 
57     @Override
onCreateBitmapLoader( final int id, final Bundle args, final String uri)58     public Loader<BitmapResult> onCreateBitmapLoader(
59             final int id, final Bundle args, final String uri) {
60         switch (id) {
61             case BITMAP_LOADER_AVATAR:
62             case BITMAP_LOADER_THUMBNAIL:
63             case BITMAP_LOADER_PHOTO:
64                 return new BuglePhotoBitmapLoader(getActivity().getContext(), uri);
65             default:
66                 LogUtil.e(LogUtil.BUGLE_TAG,
67                         "Photoviewer unable to open bitmap loader with unknown id: " + id);
68                 return null;
69         }
70     }
71 
72     @Override
updateActionBar()73     public void updateActionBar() {
74         final Cursor cursor = getCursorAtProperPosition();
75 
76         if (mSaveItem == null || cursor == null) {
77             // Load not finished, called from framework code before ready
78             return;
79         }
80         // Show the name as the title
81         mActionBarTitle = cursor.getString(PhotoViewQuery.INDEX_SENDER_FULL_NAME);
82         if (TextUtils.isEmpty(mActionBarTitle)) {
83             // If the name is not known, fall back to the phone number
84             mActionBarTitle = cursor.getString(PhotoViewQuery.INDEX_DISPLAY_DESTINATION);
85         }
86 
87         // Show the timestamp as the subtitle
88         final long receivedTimestamp = cursor.getLong(PhotoViewQuery.INDEX_RECEIVED_TIMESTAMP);
89         mActionBarSubtitle = Dates.getMessageTimeString(receivedTimestamp).toString();
90 
91         setActionBarTitles(getActivity().getActionBarInterface());
92         mSaveItem.setVisible(!isTempFile());
93 
94         updateShareActionProvider();
95     }
96 
updateShareActionProvider()97     private void updateShareActionProvider() {
98         final PhotoPagerAdapter adapter = getAdapter();
99         final Cursor cursor = getCursorAtProperPosition();
100         if (mShareActionProvider == null || mShareItem == null || adapter == null ||
101                 cursor == null) {
102             // Not enough stuff loaded to update the share action
103             return;
104         }
105         final String photoUri = adapter.getPhotoUri(cursor);
106         if (isTempFile()) {
107             mShareItem.setVisible(false);
108             return;
109         }
110         final String contentType = adapter.getContentType(cursor);
111 
112         final Intent shareIntent = new Intent();
113         shareIntent.setAction(Intent.ACTION_SEND);
114         shareIntent.setType(contentType);
115         shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(photoUri));
116         mShareActionProvider.setShareIntent(shareIntent);
117         mShareItem.setVisible(true);
118     }
119 
120     /**
121      * Checks whether the current photo is a temp file.  A temp file can be deleted at any time, so
122      * we need to disable share and save options because the file may no longer be there.
123      */
isTempFile()124     private boolean isTempFile() {
125         final Cursor cursor = getCursorAtProperPosition();
126         final Uri photoUri = Uri.parse(getAdapter().getPhotoUri(cursor));
127         return MediaScratchFileProvider.isMediaScratchSpaceUri(photoUri);
128     }
129 
130     @Override
onCreateOptionsMenu(final Menu menu)131     public boolean onCreateOptionsMenu(final Menu menu) {
132         ((Activity) getActivity()).getMenuInflater().inflate(R.menu.photo_view_menu, menu);
133 
134         // Get the ShareActionProvider
135         mShareItem = menu.findItem(R.id.action_share);
136         mShareActionProvider = (ShareActionProvider) mShareItem.getActionProvider();
137         updateShareActionProvider();
138 
139         mSaveItem = menu.findItem(R.id.action_save);
140         return true;
141     }
142 
143     @Override
onPrepareOptionsMenu(final Menu menu)144     public boolean onPrepareOptionsMenu(final Menu menu) {
145         return !mIsEmpty;
146     }
147 
148     @Override
onOptionsItemSelected(final MenuItem item)149     public boolean onOptionsItemSelected(final MenuItem item) {
150         if (item.getItemId() == R.id.action_save) {
151             if (OsUtil.hasStoragePermission()) {
152                 final PhotoPagerAdapter adapter = getAdapter();
153                 final Cursor cursor = getCursorAtProperPosition();
154                 if (cursor == null) {
155                     final Context context = getActivity().getContext();
156                     final String error = context.getResources().getQuantityString(
157                             R.plurals.attachment_save_error, 1, 1);
158                     Toast.makeText(context, error, Toast.LENGTH_SHORT).show();
159                     return true;
160                 }
161                 final String photoUri = adapter.getPhotoUri(cursor);
162                 new ConversationFragment.SaveAttachmentTask(((Activity) getActivity()),
163                         Uri.parse(photoUri), adapter.getContentType(cursor)).executeOnThreadPool();
164             } else {
165                 ((Activity)getActivity()).requestPermissions(
166                         new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, 0);
167             }
168             return true;
169         } else {
170             return super.onOptionsItemSelected(item);
171         }
172     }
173 
174     @Override
createPhotoPagerAdapter(final Context context, final FragmentManager fm, final Cursor c, final float maxScale)175     public PhotoPagerAdapter createPhotoPagerAdapter(final Context context,
176             final FragmentManager fm, final Cursor c, final float maxScale) {
177         return new BuglePhotoPageAdapter(context, fm, c, maxScale, mDisplayThumbsFullScreen);
178     }
179 }
180