/* * Copyright (C) 2015 Jorge Ruesga * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.ruesga.android.wallpapers.photophase.preferences; import android.Manifest; import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.ObjectAnimator; import android.annotation.TargetApi; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.Cursor; import android.os.AsyncTask; import android.os.AsyncTask.Status; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.preference.PreferenceFragment; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.FrameLayout; import android.widget.GridView; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.ruesga.android.wallpapers.photophase.AndroidHelper; import com.ruesga.android.wallpapers.photophase.ICastService; import com.ruesga.android.wallpapers.photophase.R; import com.ruesga.android.wallpapers.photophase.adapters.AlbumCardUiAdapter; import com.ruesga.android.wallpapers.photophase.adapters.AlbumPictureAdapter; import com.ruesga.android.wallpapers.photophase.cast.CastService; import com.ruesga.android.wallpapers.photophase.model.Album; import com.ruesga.android.wallpapers.photophase.model.Picture; import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider.Preferences; import com.ruesga.android.wallpapers.photophase.widgets.AlbumInfoView; import com.ruesga.android.wallpapers.photophase.widgets.PictureItemView; import com.ruesga.android.wallpapers.photophase.widgets.PictureItemView.CallbacksListener; import java.io.File; import java.io.IOException; import java.text.DateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; /** * A fragment class for select the picture that will be displayed on the wallpaper */ public class ChoosePicturesFragment extends PreferenceFragment implements AlbumInfoView.CallbacksListener, AlbumInfoView.CastProxy, OnClickListener, OnBackPressedListener { private static final String TAG = "ChoosePicturesFragment"; private static final boolean DEBUG = false; private static final int PROGRESS_STEPS = 5; private static final int READ_EXTERNAL_STORAGE_PERM_REQUEST = 0; private ICastService mCastService; private final ServiceConnection mCastConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder binder) { mCastService = ICastService.Stub.asInterface(binder); if (!hasNearDevices()) { try { mCastService.requestScan(); } catch (RemoteException ex) { // Ignore } } } @Override public void onServiceDisconnected(ComponentName componentName) { mCastService = null; } }; // The album loader task private class AlbumLoaderTask extends AsyncTask<Void, Album, Void> { private DateFormat mDateFormat; private final boolean mSelectAll; public AlbumLoaderTask(boolean selectAll) { mSelectAll = selectAll; } /** * {@inheritDoc} */ @Override protected Void doInBackground(Void... params) { // Query all the external content and classify the pictures in albums and load the cards mDateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); Cursor c = getActivity().getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{ MediaStore.MediaColumns.DATA }, null, null, MediaStore.MediaColumns.DATA); if (c != null) { try { long start = System.currentTimeMillis(); if (DEBUG) Log.v(TAG, "Media library:"); int count = 0; List<Album> pending = new ArrayList<>(); List<Album> all = new ArrayList<>(); Album album = null; while (c.moveToNext()) { album = processPath(all, pending, album, c.getString(0)); count++; if (count % PROGRESS_STEPS == 0) { // Notify and clean publishProgress(pending.toArray(new Album[pending.size()])); pending.clear(); } } // Add the last albums if (album != null) { // Add to global structures all.add(album); mOriginalAlbums.add((Album)album.clone()); // Add to local structures and notify pending.add(album); // Notify publishProgress(pending.toArray(new Album[pending.size()])); } long end = System.currentTimeMillis(); if (DEBUG) Log.v(TAG, "Library loaded in " + (end - start) + " miliseconds"); } finally { c.close(); } } if (mSelectAll) { Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); } return null; } /** * {@inheritDoc} */ @Override protected void onProgressUpdate(Album... albums) { Collections.addAll(mAlbums, albums); mAlbumAdapter.notifyDataSetChanged(); } /** * {@inheritDoc} */ @Override protected void onPostExecute(Void result) { mAlbumAdapter.notifyDataSetChanged(); if (mRestoreMenuItem != null) { mRestoreMenuItem.setVisible(true); } if (mInvertMenuItem != null) { mInvertMenuItem.setVisible(true); } if (mSelectAll) { Intent intent = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED); intent.putExtra(PreferencesProvider.EXTRA_FLAG_MEDIA_RELOAD, Boolean.TRUE); getActivity().sendBroadcast(intent); mRecreateWorld = true; } } /** * Method that process a album path * * @param all All the albums * @param pending Pending albums to notify * @param data The current album data * @param path The path to analyze * @return Album The new current album */ private Album processPath(List<Album> all, List<Album> pending, Album data, String path) { // Only valid files (those i can read) if (DEBUG) Log.v(TAG, "\t" + path); Album album = data; if (path != null) { File f = new File(path); if (f.isFile() && f.canRead()) { File p = f.getParentFile(); String name = p.getName(); if (album == null || album.getPath().compareTo(p.getAbsolutePath()) != 0) { if (album != null) { // Add to global structures all.add(album); mOriginalAlbums.add((Album)album.clone()); // Add to local structures and notify pending.add(album); } album = new Album(); album.setPath(p.getAbsolutePath()); album.setName(name); album.setDate(mDateFormat.format(new Date(p.lastModified()))); album.setSelected(isSelectedItem(album.getPath())); album.setItems(new ArrayList<Picture>()); album.setSelectedItems(new ArrayList<String>()); } boolean selected = isSelectedItem(f.getAbsolutePath()); album.getItems().add(new Picture(f.getAbsolutePath(), selected)); if (selected) { album.getSelectedItems().add(f.getAbsolutePath()); } } } if (mSelectAll) { album.setSelected(true); synchronized (mSelectedAlbums) { mSelectedAlbums.add(album.getPath()); } } return album; } /** * Method that checks if an item is selected * * @param item The item * @return boolean if an item is selected */ private boolean isSelectedItem(String item) { synchronized (mSelectedAlbums) { for (String albumPath : mSelectedAlbums) { if (item.compareTo(albumPath) == 0) { return true; } } } return false; } } private AlbumLoaderTask mTask; private final Handler.Callback mCallback = new Handler.Callback() { @Override public boolean handleMessage(Message msg) { if (msg.what == MSG_LOAD_PICTURES) { loadPictures((Album)msg.obj); return true; } return false; } }; private final OnItemClickListener mOnItemClickListener = new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (mShowingAlbums) { onHeaderPressed(parent, view, position); } } }; private final CallbacksListener mPictureListener = new CallbacksListener() { @Override public void onPictureItemViewPressed(View v) { onPicturePressed(v); } }; private static final int MSG_LOAD_PICTURES = 1; private List<Album> mAlbums; private List<Album> mOriginalAlbums; private final Set<String> mSelectedAlbums = new HashSet<>(); private final Set<String> mOriginalSelectedAlbums = new HashSet<>(); private ViewGroup mContainer; private View mEmpty; private ListView mAlbumsPanel; private AlbumCardUiAdapter mAlbumAdapter; private GridView mPicturesPanel; private AlbumPictureAdapter mPictureAdapter; private boolean mSelectionChanged; private boolean mRecreateWorld; // Animation references private ViewGroup mSrcParent; private View mSrcView; private ViewGroup mDstParent; private View mDstView; private Album mAlbum; private int mPicturesAnimDurationIn; private int mPicturesAnimDurationOut; private Handler mHandler; private LayoutInflater mInflater; private boolean mShowingAlbums; private MenuItem mRestoreMenuItem; private MenuItem mInvertMenuItem; private boolean mIsViewCreated; private boolean mIsAttached; /** * {@inheritDoc} */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mHandler = new Handler(mCallback); mShowingAlbums = true; // Create an empty album mAlbums = new ArrayList<>(); mOriginalAlbums = new ArrayList<>(); // Change the preference manager getPreferenceManager().setSharedPreferencesName(PreferencesProvider.PREFERENCES_FILE); getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); // Load the albums user selection mOriginalSelectedAlbums.addAll(removeObsoleteAlbumsData( Preferences.Media.getSelectedMedia(getActivity()))); mSelectedAlbums.addAll(mOriginalSelectedAlbums); mSelectionChanged = false; final Resources res = getResources(); mPicturesAnimDurationIn = res.getInteger(R.integer.pictures_anim_in); mPicturesAnimDurationOut = res.getInteger(R.integer.pictures_anim_out); setHasOptionsMenu(true); } /** * {@inheritDoc} */ @Override public void onDestroy() { unbindDrawables(mAlbumsPanel); unregister(); if (!mShowingAlbums) { mPicturesPanel.setVisibility(View.GONE); mDstView.setVisibility(View.GONE); mDstParent.removeView(mPicturesPanel); mDstParent.removeView(mDstView); } // Notify that the settings was changed Intent intent = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED); if (mSelectionChanged) { intent.putExtra(PreferencesProvider.EXTRA_FLAG_REDRAW, Boolean.TRUE); intent.putExtra(PreferencesProvider.EXTRA_FLAG_EMPTY_TEXTURE_QUEUE, Boolean.TRUE); intent.putExtra(PreferencesProvider.EXTRA_FLAG_MEDIA_RELOAD, Boolean.TRUE); } if (mRecreateWorld) { intent.putExtra(PreferencesProvider.EXTRA_FLAG_RECREATE_WORLD, Boolean.TRUE); } getActivity().sendBroadcast(intent); super.onDestroy(); } private void unregister() { mAlbums.clear(); mOriginalAlbums.clear(); } /** * Method that unbind all the drawables for a view * * @param view The root view */ private void unbindDrawables(View view) { if (view.getBackground() != null) { view.getBackground().setCallback(null); } if (view instanceof ViewGroup) { for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { unbindDrawables(((ViewGroup) view).getChildAt(i)); } } } /** * {@inheritDoc} */ @Override public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) { mContainer = container; mInflater = inflater; // Inflate the layout for this fragment FrameLayout root = (FrameLayout)mInflater.inflate( R.layout.choose_picture_fragment, container, false); mEmpty = root.findViewById(android.R.id.empty); mEmpty.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (!requestAlbumData(getActivity(), false, false)) { requestStoragePermission(true); } } }); if (getActivity() != null) { updateEmptyMsg(AndroidHelper.hasReadExternalStoragePermissionGranted(getActivity())); } mAlbumsPanel = (ListView)root.findViewById(R.id.albums_panel); mAlbumsPanel.setSmoothScrollbarEnabled(true); mAlbumsPanel.setEmptyView(mEmpty); mAlbumAdapter = new AlbumCardUiAdapter(getActivity(), mAlbums, this, this); mAlbumsPanel.setAdapter(mAlbumAdapter); mAlbumsPanel.setOnItemClickListener(mOnItemClickListener); // Force Hardware acceleration if (!root.isHardwareAccelerated()) { root.setLayerType(View.LAYER_TYPE_HARDWARE, null); } if (!mAlbumsPanel.isHardwareAccelerated()) { mAlbumsPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null); } // Load the albums unregister(); mIsViewCreated = true; onViewCreatedAndAttached(getActivity()); return root; } @Override public void onDestroyView() { super.onDestroyView(); mIsViewCreated = false; } /** * {@inheritDoc} */ @Override @SuppressWarnings("deprecation") public void onAttach(Activity activity) { super.onAttach(activity); mIsAttached = true; onViewCreatedAndAttached(getActivity()); } private void onViewCreatedAndAttached(Context context) { if (mIsAttached && mIsViewCreated) { if (!requestAlbumData(context, false, false)) { requestStoragePermission(false); } if (PreferencesProvider.Preferences.Cast.isEnabled(getActivity())) { try { Intent i = new Intent(getActivity(), CastService.class); getActivity().bindService(i, mCastConnection, Context.BIND_AUTO_CREATE); } catch (SecurityException se) { Log.w(TAG, "Can't bound to CastService", se); } } } } /** * {@inheritDoc} */ @Override public void onDetach() { if (mTask != null && mTask.getStatus().compareTo(Status.FINISHED) != 0) { mTask.cancel(true); } if (mCastService != null) { getActivity().unbindService(mCastConnection); } super.onDetach(); mIsAttached = false; } /** * {@inheritDoc} */ @Override public void onClick(View v) { // Hide the albums picture with animation if (v.equals(mDstView)) { hideAlbumPictures(mDstParent, mDstView, mSrcParent, mSrcView); } } /** * {@inheritDoc} */ @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.albums, menu); mRestoreMenuItem = menu.findItem(R.id.mnu_restore); mInvertMenuItem = menu.findItem(R.id.mnu_invert); if (mRestoreMenuItem != null) { mRestoreMenuItem.setVisible(false); } if (mInvertMenuItem != null) { mInvertMenuItem.setVisible(false); } } /** * {@inheritDoc} */ @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.mnu_ok: getActivity().finish(); return true; case R.id.mnu_restore: restoreData(); return true; case R.id.mnu_invert: if (mShowingAlbums) { invertAll(); } else { invertAlbum(mAlbum); } return true; default: return super.onOptionsItemSelected(item); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case READ_EXTERNAL_STORAGE_PERM_REQUEST: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { requestAlbumData(getActivity(), true, true); } break; } } private boolean requestAlbumData(Context context, boolean ignoreGrants, boolean selectAll) { if (mTask != null && mTask.getStatus().compareTo(Status.FINISHED) != 0) { mTask.cancel(true); } if (ignoreGrants || AndroidHelper.hasReadExternalStoragePermissionGranted(context)) { updateEmptyMsg(true); mTask = new AlbumLoaderTask(selectAll); mTask.execute(); return true; } else { updateEmptyMsg(false); } return false; } @TargetApi(value= Build.VERSION_CODES.M) private void requestStoragePermission(boolean manual) { if (!manual && shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) { return; } requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, READ_EXTERNAL_STORAGE_PERM_REQUEST); } private void updateEmptyMsg(boolean hasStorageGrants) { if (mEmpty != null) { TextView title = (TextView) mEmpty.findViewById(R.id.empty_title); TextView msg = (TextView) mEmpty.findViewById(R.id.empty_msg); if (hasStorageGrants) { title.setText(R.string.no_pictures_albums_found_msg); msg.setText(R.string.no_pictures_albums_tap_to_refresh_msg); } else { title.setText(R.string.no_pictures_albums_not_granted_permission_msg); msg.setText(R.string.no_pictures_albums_tap_to_request_permission_msg); } } } /** * Method that restores the albums to its original state */ private void restoreData() { // Restore and the albums the selection synchronized (mSelectedAlbums) { mSelectedAlbums.clear(); mSelectedAlbums.addAll(mOriginalSelectedAlbums); } int count = Math.min(mAlbums.size(), mOriginalAlbums.size()); for (int i = 0; i < count ; i++) { Album album = mAlbums.get(i); Album originalAlbum = mOriginalAlbums.get(i); // Update selected status album.setSelected(originalAlbum.isSelected()); album.setItems(new ArrayList<>(originalAlbum.getItems())); album.setSelectedItems(new ArrayList<>(originalAlbum.getSelectedItems())); } mAlbumAdapter.notifyDataSetChanged(); // Update settings Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); mSelectionChanged = true; if (!mShowingAlbums) { hideAlbumPictures(mDstParent, mDstView, mSrcParent, mSrcView); } } /** * Method that inverts the selection of all the albums */ private void invertAll() { // Restore and the albums the selection synchronized (mSelectedAlbums) { mSelectedAlbums.clear(); for (Album album : mAlbums) { album.setSelected(!album.isSelected()); album.setSelectedItems(new ArrayList<String>()); if (album.isSelected()) { mSelectedAlbums.add(album.getPath()); } else { mSelectedAlbums.addAll(album.getSelectedItems()); } for (Picture picture : album.getItems()) { picture.setSelected(false); } } } mAlbumAdapter.notifyDataSetChanged(); // Update settings Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); mSelectionChanged = true; } /** * Method that inverts the selection of an album * * @param album The album which to invert its selection */ private void invertAlbum(Album album) { // Remove all pictures of the album removeAlbumItems(album); List<String> origSelectedItems = new ArrayList<>(album.getSelectedItems()); List<String> selectedItems = album.getSelectedItems(); album.getSelectedItems().clear(); for (Picture picture : album.getItems()) { boolean selected = !origSelectedItems.contains(picture.getPath()); if (selected) { selectedItems.add(picture.getPath()); } picture.setSelected(selected); } // Notify pictures dataset changed updateAlbumInfo(mDstView, album); mPictureAdapter.notifyViewChanged(); synchronized (mSelectedAlbums) { mSelectedAlbums.addAll(album.getSelectedItems()); } Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); mSelectionChanged = true; } /** * {@inheritDoc} */ @Override public void onAlbumSelected(Album album) { updateAlbumSelection(album, true); } /** * {@inheritDoc} */ @Override public void onAlbumDeselected(Album album) { updateAlbumSelection(album, false); } /** * {@inheritDoc} */ @Override public void onAllPicturesSelected(Album album) { updateAllPicturesSelection(album, true); } /** * {@inheritDoc} */ @Override public void onAllPicturesDeselected(Album album) { updateAllPicturesSelection(album, false); } /** * {@inheritDoc} */ @Override public boolean onBackPressed() { if (!mShowingAlbums) { // Hide album pictures hideAlbumPictures(mDstParent, mDstView, mSrcParent, mSrcView); return true; } return false; } /** * Method that update the album selection * * @param album The album to update * @param selected If the album is selected */ private void updateAlbumSelection(Album album, boolean selected) { // Remove all pictures of the album removeAlbumItems(album); album.setSelected(selected); album.getSelectedItems().clear(); for (Picture picture : album.getItems()) { picture.setSelected(false); } if (selected) { synchronized (mSelectedAlbums) { mSelectedAlbums.add(album.getPath()); } } if (!mShowingAlbums) { // Notify pictures dataset changed updateAlbumInfo(mDstView, album); mPictureAdapter.notifyViewChanged(); } else { mAlbumAdapter.notifyDataSetChanged(); } Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); mSelectionChanged = true; } /** * Method that update the whole picture selection * * @param album The album to update * @param selected If all the picture were selected */ private void updateAllPicturesSelection(Album album, boolean selected) { // Remove all pictures of the album removeAlbumItems(album); List<String> selectedItems = album.getSelectedItems(); selectedItems.clear(); for (Picture picture : album.getItems()) { if (selected) { selectedItems.add(picture.getPath()); } picture.setSelected(selected); } album.setSelected(false); synchronized (mSelectedAlbums) { mSelectedAlbums.addAll(album.getSelectedItems()); } Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); mSelectionChanged = true; // Notify pictures dataset changed updateAlbumInfo(mDstView, album); mPictureAdapter.notifyViewChanged(); } /** * Method that removes the reference to all the items and itself * * @param ref The album */ private void removeAlbumItems(Album ref) { synchronized (mSelectedAlbums) { Iterator<String> it = mSelectedAlbums.iterator(); while (it.hasNext()) { String item = it.next(); String parent = new File(item).getParent(); if (parent.compareTo(ref.getPath()) == 0) { it.remove(); } else if (item.compareTo(ref.getPath()) == 0) { it.remove(); } } } } /** * Method that shows the album pictures while animating the view * * @param srcParent The source parent view * @param srcView The source view * @param dstParent The destination parent view * @param dstView The destination view */ private void showAlbumPictures(final ViewGroup srcParent, final View srcView, final ViewGroup dstParent, final View dstView) { // Hide the source view srcView.setAlpha(0.0f); // Animation from bottom to top ObjectAnimator anim1 = ObjectAnimator.ofFloat(dstView, "translationY", srcView.getY(), srcParent.getPaddingTop()); anim1.setDuration(mPicturesAnimDurationIn); anim1.setInterpolator(new AccelerateDecelerateInterpolator()); anim1.addListener(new AnimatorListener() { @Override public void onAnimationStart(Animator animation) { // Ignore } @Override public void onAnimationRepeat(Animator animation) { // Ignore } @Override public void onAnimationEnd(Animator animation) { // Re-layout the view in its new position dstView.setOnClickListener(ChoosePicturesFragment.this); // And now finally show the new album pictures layout // and fill it mPicturesPanel.setVisibility(View.VISIBLE); mHandler.sendMessage(mHandler.obtainMessage(MSG_LOAD_PICTURES, mAlbum)); mShowingAlbums = false; } @Override public void onAnimationCancel(Animator animation) { // Ignore } }); anim1.start(); // Hide the parent view AlphaAnimation anim2 = new AlphaAnimation(1.0f, 0.0f); anim2.setDuration(mPicturesAnimDurationIn); anim2.setFillAfter(true); anim2.setZAdjustment(Animation.ZORDER_BOTTOM); anim2.setInterpolator(new AccelerateDecelerateInterpolator()); srcParent.setEnabled(false); srcParent.startAnimation(anim2); // Save the references mSrcParent = srcParent; mSrcView = srcView; mDstParent = dstParent; mDstView = dstView; } /** * Method that hides the album pictures while animating the view * * @param srcParent The source parent view * @param srcView The source view * @param dstParent The destination parent view * @param dstView The destination view */ private void hideAlbumPictures(final ViewGroup srcParent, final View srcView, final ViewGroup dstParent, final View dstView) { mPicturesPanel.setVisibility(View.INVISIBLE); // Animation from top to bottom ObjectAnimator anim1 = ObjectAnimator.ofFloat(srcView, "translationY", dstParent.getPaddingTop(), dstView.getY()); anim1.setDuration(mPicturesAnimDurationOut); anim1.setInterpolator(new AccelerateDecelerateInterpolator()); anim1.addListener(new AnimatorListener() { @Override public void onAnimationStart(Animator animation) { // Ignore } @Override public void onAnimationRepeat(Animator animation) { // Ignore } @Override public void onAnimationEnd(Animator animation) { // Remove the source view and show the destination view srcParent.removeView(srcView); dstView.setAlpha(1.0f); dstParent.setEnabled(true); unbindDrawables(mPicturesPanel); srcParent.removeView(mPicturesPanel); mAlbumAdapter.notifyDataSetChanged(); mShowingAlbums = true; } @Override public void onAnimationCancel(Animator animation) { // Ignore } }); anim1.start(); // Hide the parent view AlphaAnimation anim2 = new AlphaAnimation(0.0f, 1.0f); anim2.setDuration(mPicturesAnimDurationOut); anim2.setFillAfter(true); anim2.setZAdjustment(Animation.ZORDER_BOTTOM); anim2.setInterpolator(new AccelerateDecelerateInterpolator()); dstParent.startAnimation(anim2); } /** * Method that updates an album info * * @param v The header view * @param album The album data */ private void updateAlbumInfo(View v, Album album) { final Resources res = getResources(); AlbumInfoView info = (AlbumInfoView)v.findViewById(R.id.album_info); info.setAlbum(album); ImageView icon = (ImageView)info.findViewById(R.id.album_thumbnail); TextView name = (TextView)info.findViewById(R.id.album_name); TextView items = (TextView)info.findViewById(R.id.album_items); TextView selectedItems = (TextView)info.findViewById(R.id.album_selected_items); icon.setImageDrawable(album.getIcon()); name.setText(album.getName()); int size = album.getItems().size(); items.setText(String.format(res.getQuantityText( R.plurals.album_number_of_pictures, size).toString(), size)); int selected = album.getSelectedItems().size(); String count = String.valueOf(selected); if (selected > 99) { count = "99+"; } selectedItems.setText(count); selectedItems.setVisibility(!album.isSelected() ? View.VISIBLE : View.INVISIBLE); info.setSelected(album.isSelected()); } /** * Method that load all the pictures of the album * * @param album The album for which load all its pictures */ private void loadPictures(Album album) { List<Picture> items = album.getItems(); mPictureAdapter = new AlbumPictureAdapter( getActivity(), album, items, mPicturesPanel, mPictureListener); mPicturesPanel.setAdapter(mPictureAdapter); } /** * Method invoked when an album header was pressed * * @param parent The parent view * @param view The header view * @param position The position */ private void onHeaderPressed(AdapterView<?> parent, View view, int position) { mAlbum = mAlbumAdapter.getItem(position); File path = new File(mAlbum.getPath()); if (!path.exists()) { Toast.makeText(getActivity(), R.string.pref_media_album_not_exists, Toast.LENGTH_SHORT).show(); updateAlbumSelection(mAlbum, false); mAlbums.remove(mAlbum); mAlbumAdapter.notifyDataSetChanged(); return; } // Header view View header = mInflater.inflate(R.layout.album_info, mContainer, false); header.setTranslationY(view.getY() + parent.getPaddingTop()); header.setLayoutParams(new ViewGroup.LayoutParams(view.getWidth(), view.getHeight())); // Pictures view mPicturesPanel = (GridView) mInflater.inflate(R.layout.pictures_view, mContainer, false); if (!mPicturesPanel.isHardwareAccelerated()) { mPicturesPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null); } mPicturesPanel.setY(view.getHeight()); mPicturesPanel.setLayoutParams(new ViewGroup.LayoutParams(view.getWidth(), parent.getHeight() - view.getHeight() - parent.getPaddingTop() - parent.getPaddingBottom())); mPicturesPanel.setVisibility(View.INVISIBLE); // Add to the container mContainer.addView(mPicturesPanel); mContainer.addView(header); // Update and display the album pictures view AlbumInfoView info = (AlbumInfoView)header.findViewById(R.id.album_info); info.addCallBackListener(ChoosePicturesFragment.this); info.setCastProxy(ChoosePicturesFragment.this); info.setAlbumMode(false); updateAlbumInfo(header, mAlbum); showAlbumPictures(parent, view, mContainer, header); } /** * Method invoked when a picture view was pressed * * @param view The picture view */ private void onPicturePressed(View view) { PictureItemView pictureView = (PictureItemView)view.findViewById(R.id.picture); if (pictureView != null) { Picture picture = pictureView.getPicture(); onPictureChanged(picture); // Notify all the views pictureView.updateView(picture, mAlbum.getSelectedItems().size() > 0, false); updateAlbumInfo(mDstView, mAlbum); mAlbumAdapter.notifyDataSetChanged(); mPictureAdapter.notifyViewChanged(); // Update settings synchronized (mSelectedAlbums) { mSelectedAlbums.addAll(mAlbum.getSelectedItems()); } Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); mSelectionChanged = true; } } private void onPictureChanged(Picture picture) { removeAlbumItems(mAlbum); List<String> selectedItems = mAlbum.getSelectedItems(); if (selectedItems.contains(picture.getPath())) { selectedItems.remove(picture.getPath()); picture.setSelected(false); } else { selectedItems.add(picture.getPath()); picture.setSelected(true); mAlbum.setSelected(false); } } /** * Method that removes all the nonexistent albums and pictures * * @param data The data to filter * @return Set<String> The data filtered */ private Set<String> removeObsoleteAlbumsData(Set<String> data) { Set<String> validDataList = new HashSet<>(); for (String val : data) { File f = new File(val); if (f.exists()) { try { validDataList.add(f.getCanonicalPath()); } catch (IOException ioex) { // Ignore } } } if (data.size() != validDataList.size()) { // Obsolete entries were removed data.clear(); data.addAll(validDataList); Preferences.Media.setSelectedMedia(getActivity(), mOriginalSelectedAlbums); } return data; } public boolean hasNearDevices() { if (mCastService != null) { try { return mCastService.hasNearDevices(); } catch (RemoteException ex) { // Ignore } } return false; } @Override public void enqueue(Album album) { if (mCastService != null) { try { mCastService.enqueue(album.getPath()); } catch (RemoteException ex) { // Ignore } } } @Override public void cast(Album album) { if (mCastService != null) { try { mCastService.cast(album.getPath()); } catch (RemoteException ex) { // Ignore } } } }