blob: f303f9364abd3328cc0c1537954d3001b1c6e28b [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* 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.android.wallpaperpicker;
import android.animation.LayoutTransition;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.util.Pair;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLayoutChangeListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import com.android.wallpaperpicker.tileinfo.DefaultWallpaperInfo;
import com.android.wallpaperpicker.tileinfo.LiveWallpaperInfo;
import com.android.wallpaperpicker.tileinfo.PickImageInfo;
import com.android.wallpaperpicker.tileinfo.ResourceWallpaperInfo;
import com.android.wallpaperpicker.tileinfo.ThirdPartyWallpaperInfo;
import com.android.wallpaperpicker.tileinfo.UriWallpaperInfo;
import com.android.wallpaperpicker.tileinfo.WallpaperTileInfo;
import java.util.ArrayList;
import java.util.List;
public class WallpaperPickerActivity extends WallpaperCropActivity
implements OnClickListener, OnLongClickListener, ActionMode.Callback {
static final String TAG = "WallpaperPickerActivity";
public static final int IMAGE_PICK = 5;
public static final int PICK_WALLPAPER_THIRD_PARTY_ACTIVITY = 6;
private static final String TEMP_WALLPAPER_TILES = "TEMP_WALLPAPER_TILES";
private static final String SELECTED_INDEX = "SELECTED_INDEX";
private static final int FLAG_POST_DELAY_MILLIS = 200;
private View mSelectedTile;
private LinearLayout mWallpapersView;
private HorizontalScrollView mWallpaperScrollContainer;
private View mWallpaperStrip;
private ActionMode mActionMode;
ArrayList<Uri> mTempWallpaperTiles = new ArrayList<Uri>();
private SavedWallpaperImages mSavedImages;
private int mSelectedIndex = -1;
private float mWallpaperParallaxOffset;
/**
* shows the system wallpaper behind the window and hides the {@link #mCropView} if visible
* @param visible should the system wallpaper be shown
*/
protected void setSystemWallpaperVisiblity(final boolean visible) {
// hide our own wallpaper preview if necessary
if(!visible) {
mCropView.setVisibility(View.VISIBLE);
} else {
changeWallpaperFlags(visible);
}
// the change of the flag must be delayed in order to avoid flickering,
// a simple post / double post does not suffice here
mCropView.postDelayed(new Runnable() {
@Override
public void run() {
if(!visible) {
changeWallpaperFlags(visible);
} else {
mCropView.setVisibility(View.INVISIBLE);
}
}
}, FLAG_POST_DELAY_MILLIS);
}
private void changeWallpaperFlags(boolean visible) {
int desiredWallpaperFlag = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0;
int currentWallpaperFlag = getWindow().getAttributes().flags
& WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
if (desiredWallpaperFlag != currentWallpaperFlag) {
getWindow().setFlags(desiredWallpaperFlag,
WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
}
}
@Override
protected void onLoadRequestComplete(LoadRequest req, boolean success) {
super.onLoadRequestComplete(req, success);
if (success) {
setSystemWallpaperVisiblity(false);
}
}
/**
* called by onCreate; this is sub-classed to overwrite WallpaperCropActivity
*/
protected void init() {
setContentView(R.layout.wallpaper_picker);
mCropView = (CropView) findViewById(R.id.cropView);
mCropView.setVisibility(View.INVISIBLE);
mProgressView = findViewById(R.id.loading);
mWallpaperScrollContainer = (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container);
mWallpaperStrip = findViewById(R.id.wallpaper_strip);
mCropView.setTouchCallback(new ToggleOnTapCallback(mWallpaperStrip));
mWallpaperParallaxOffset = getIntent().getFloatExtra(
WallpaperUtils.EXTRA_WALLPAPER_OFFSET, 0);
mWallpapersView = (LinearLayout) findViewById(R.id.wallpaper_list);
// Populate the saved wallpapers
mSavedImages = new SavedWallpaperImages(this);
populateWallpapers(mWallpapersView, mSavedImages.loadThumbnailsAndImageIdList(), true);
// Populate the built-in wallpapers
ArrayList<WallpaperTileInfo> wallpapers = findBundledWallpapers();
populateWallpapers(mWallpapersView, wallpapers, false);
// Load live wallpapers asynchronously
new LiveWallpaperInfo.LoaderTask(this) {
@Override
protected void onPostExecute(List<LiveWallpaperInfo> result) {
populateWallpapers((LinearLayout) findViewById(R.id.live_wallpaper_list),
result, false);
initializeScrollForRtl();
updateTileIndices();
}
}.execute();
// Populate the third-party wallpaper pickers
populateWallpapers((LinearLayout) findViewById(R.id.third_party_wallpaper_list),
ThirdPartyWallpaperInfo.getAll(this), false /* addLongPressHandler */);
// Add a tile for the Gallery
LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list);
masterWallpaperList.addView(
createTileView(masterWallpaperList, new PickImageInfo(), false), 0);
// Select the first item; wait for a layout pass so that we initialize the dimensions of
// cropView or the defaultWallpaperView first
mCropView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if ((right - left) > 0 && (bottom - top) > 0) {
if (mSelectedIndex >= 0 && mSelectedIndex < mWallpapersView.getChildCount()) {
onClick(mWallpapersView.getChildAt(mSelectedIndex));
setSystemWallpaperVisiblity(false);
}
v.removeOnLayoutChangeListener(this);
}
}
});
updateTileIndices();
// Update the scroll for RTL
initializeScrollForRtl();
// Create smooth layout transitions for when items are deleted
final LayoutTransition transitioner = new LayoutTransition();
transitioner.setDuration(200);
transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
transitioner.setAnimator(LayoutTransition.DISAPPEARING, null);
mWallpapersView.setLayoutTransition(transitioner);
// Action bar
// Show the custom action bar view
final ActionBar actionBar = getActionBar();
actionBar.setCustomView(R.layout.actionbar_set_wallpaper);
actionBar.getCustomView().setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
// Ensure that a tile is selected and loaded.
if (mSelectedTile != null && mCropView.getTileSource() != null) {
// Prevent user from selecting any new tile.
mWallpaperStrip.setVisibility(View.GONE);
actionBar.hide();
WallpaperTileInfo info = (WallpaperTileInfo) mSelectedTile.getTag();
info.onSave(WallpaperPickerActivity.this);
} else {
// This case shouldn't be possible, since "Set wallpaper" is disabled
// until user clicks on a title.
Log.w(TAG, "\"Set wallpaper\" was clicked when no tile was selected");
}
}
});
mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
}
/**
* Called when a wallpaper tile is clicked
*/
@Override
public void onClick(View v) {
if (mActionMode != null) {
// When CAB is up, clicking toggles the item instead
if (v.isLongClickable()) {
onLongClick(v);
}
return;
}
WallpaperTileInfo info = (WallpaperTileInfo) v.getTag();
if (info.isSelectable() && v.getVisibility() == View.VISIBLE) {
selectTile(v);
setWallpaperButtonEnabled(true);
}
info.onClick(this);
}
/**
* Called when a view is long clicked
*/
@Override
public boolean onLongClick(View v) {
CheckableFrameLayout c = (CheckableFrameLayout) v;
c.toggle();
if (mActionMode != null) {
mActionMode.invalidate();
} else {
// Start the CAB using the ActionMode.Callback defined below
mActionMode = startActionMode(this);
int childCount = mWallpapersView.getChildCount();
for (int i = 0; i < childCount; i++) {
mWallpapersView.getChildAt(i).setSelected(false);
}
}
return true;
}
public void setWallpaperButtonEnabled(boolean enabled) {
mSetWallpaperButton.setEnabled(enabled);
}
public float getWallpaperParallaxOffset() {
return mWallpaperParallaxOffset;
}
public void selectTile(View v) {
if (mSelectedTile != null) {
mSelectedTile.setSelected(false);
mSelectedTile = null;
}
mSelectedTile = v;
v.setSelected(true);
mSelectedIndex = mWallpapersView.indexOfChild(v);
// TODO: Remove this once the accessibility framework and
// services have better support for selection state.
v.announceForAccessibility(
getString(R.string.announce_selection, v.getContentDescription()));
}
private void initializeScrollForRtl() {
if (WallpaperUtils.isRtl(getResources())) {
final ViewTreeObserver observer = mWallpaperScrollContainer.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
LinearLayout masterWallpaperList =
(LinearLayout) findViewById(R.id.master_wallpaper_list);
mWallpaperScrollContainer.scrollTo(masterWallpaperList.getWidth(), 0);
mWallpaperScrollContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}
}
public void onStop() {
super.onStop();
mWallpaperStrip = findViewById(R.id.wallpaper_strip);
if (mWallpaperStrip.getAlpha() < 1f) {
mWallpaperStrip.setAlpha(1f);
mWallpaperStrip.setVisibility(View.VISIBLE);
}
}
public void onSaveInstanceState(Bundle outState) {
outState.putParcelableArrayList(TEMP_WALLPAPER_TILES, mTempWallpaperTiles);
outState.putInt(SELECTED_INDEX, mSelectedIndex);
}
protected void onRestoreInstanceState(Bundle savedInstanceState) {
ArrayList<Uri> uris = savedInstanceState.getParcelableArrayList(TEMP_WALLPAPER_TILES);
for (Uri uri : uris) {
addTemporaryWallpaperTile(uri, true);
}
mSelectedIndex = savedInstanceState.getInt(SELECTED_INDEX, -1);
}
private void updateTileIndices() {
LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list);
final int childCount = masterWallpaperList.getChildCount();
final Resources res = getResources();
// Do two passes; the first pass gets the total number of tiles
int numTiles = 0;
for (int passNum = 0; passNum < 2; passNum++) {
int tileIndex = 0;
for (int i = 0; i < childCount; i++) {
View child = masterWallpaperList.getChildAt(i);
LinearLayout subList;
int subListStart;
int subListEnd;
if (child.getTag() instanceof WallpaperTileInfo) {
subList = masterWallpaperList;
subListStart = i;
subListEnd = i + 1;
} else { // if (child instanceof LinearLayout) {
subList = (LinearLayout) child;
subListStart = 0;
subListEnd = subList.getChildCount();
}
for (int j = subListStart; j < subListEnd; j++) {
WallpaperTileInfo info = (WallpaperTileInfo) subList.getChildAt(j).getTag();
if (info.isNamelessWallpaper()) {
if (passNum == 0) {
numTiles++;
} else {
CharSequence label = res.getString(
R.string.wallpaper_accessibility_name, ++tileIndex, numTiles);
info.onIndexUpdated(label);
}
}
}
}
}
}
private void addTemporaryWallpaperTile(final Uri uri, boolean fromRestore) {
// Add a tile for the image picked from Gallery, reusing the existing tile if there is one.
View imageTile = null;
int indexOfExistingTile = 0;
for (; indexOfExistingTile < mWallpapersView.getChildCount(); indexOfExistingTile++) {
View thumbnail = mWallpapersView.getChildAt(indexOfExistingTile);
Object tag = thumbnail.getTag();
if (tag instanceof UriWallpaperInfo && ((UriWallpaperInfo) tag).mUri.equals(uri)) {
imageTile = thumbnail;
break;
}
}
final UriWallpaperInfo info;
if (imageTile != null) {
// Always move the existing wallpaper to the front so user can see it without scrolling.
mWallpapersView.removeViewAt(indexOfExistingTile);
info = (UriWallpaperInfo) imageTile.getTag();
} else {
// This is the first time this temporary wallpaper has been added
info = new UriWallpaperInfo(uri);
imageTile = createTileView(mWallpapersView, info, true);
mTempWallpaperTiles.add(uri);
}
mWallpapersView.addView(imageTile, 0);
info.loadThumbnaleAsync(this);
updateTileIndices();
if (!fromRestore) {
onClick(imageTile);
}
}
private void populateWallpapers(ViewGroup parent, List<? extends WallpaperTileInfo> wallpapers,
boolean addLongPressHandler) {
for (WallpaperTileInfo info : wallpapers) {
parent.addView(createTileView(parent, info, addLongPressHandler));
}
}
private View createTileView(ViewGroup parent, WallpaperTileInfo info, boolean addLongPress) {
View view = info.createView(this, getLayoutInflater(), parent);
view.setTag(info);
if (addLongPress) {
view.setOnLongClickListener(this);
}
view.setOnClickListener(this);
return view;
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == IMAGE_PICK && resultCode == Activity.RESULT_OK) {
if (data != null && data.getData() != null) {
Uri uri = data.getData();
addTemporaryWallpaperTile(uri, false);
}
} else if (requestCode == PICK_WALLPAPER_THIRD_PARTY_ACTIVITY
&& resultCode == Activity.RESULT_OK) {
// Something was set on the third-party activity.
setResult(Activity.RESULT_OK);
finish();
}
}
public ArrayList<WallpaperTileInfo> findBundledWallpapers() {
final ArrayList<WallpaperTileInfo> bundled = new ArrayList<WallpaperTileInfo>(24);
Pair<ApplicationInfo, Integer> r = getWallpaperArrayResourceId();
if (r != null) {
try {
Resources wallpaperRes = getPackageManager().getResourcesForApplication(r.first);
addWallpapers(bundled, wallpaperRes, r.first.packageName, r.second);
} catch (PackageManager.NameNotFoundException e) {
}
}
// Add an entry for the default wallpaper (stored in system resources)
WallpaperTileInfo defaultWallpaperInfo = DefaultWallpaperInfo.get(this);
if (defaultWallpaperInfo != null) {
bundled.add(0, defaultWallpaperInfo);
}
return bundled;
}
public Pair<ApplicationInfo, Integer> getWallpaperArrayResourceId() {
return new Pair<>(getApplicationInfo(), R.array.wallpapers);
}
public void addWallpapers(ArrayList<WallpaperTileInfo> known, Resources res,
String packageName, int listResId) {
final String[] extras = res.getStringArray(listResId);
for (String extra : extras) {
int resId = res.getIdentifier(extra, "drawable", packageName);
if (resId != 0) {
final int thumbRes = res.getIdentifier(extra + "_small", "drawable", packageName);
if (thumbRes != 0) {
ResourceWallpaperInfo wallpaperInfo =
new ResourceWallpaperInfo(res, resId, res.getDrawable(thumbRes));
known.add(wallpaperInfo);
// Log.d(TAG, "add: [" + packageName + "]: " + extra + " (" + res + ")");
}
} else {
Log.e(TAG, "Couldn't find wallpaper " + extra);
}
}
}
public SavedWallpaperImages getSavedImages() {
return mSavedImages;
}
public void startActivityForResultSafely(Intent intent, int requestCode) {
startActivityForResult(intent, requestCode);
}
// CAB for deleting items
/**
* Called when the action mode is created; startActionMode() was called
*/
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate a menu resource providing context menu items
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.cab_delete_wallpapers, menu);
return true;
}
/**
* Called each time the action mode is shown. Always called after onCreateActionMode,
* but may be called multiple times if the mode is invalidated.
*/
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
int childCount = mWallpapersView.getChildCount();
int numCheckedItems = 0;
for (int i = 0; i < childCount; i++) {
CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
if (c.isChecked()) {
numCheckedItems++;
}
}
if (numCheckedItems == 0) {
mode.finish();
return true;
} else {
mode.setTitle(getResources().getQuantityString(
R.plurals.number_of_items_selected, numCheckedItems, numCheckedItems));
return true;
}
}
/**
* Called when the user selects a contextual menu item
*/
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.menu_delete) {
int childCount = mWallpapersView.getChildCount();
ArrayList<View> viewsToRemove = new ArrayList<View>();
boolean selectedTileRemoved = false;
for (int i = 0; i < childCount; i++) {
CheckableFrameLayout c =
(CheckableFrameLayout) mWallpapersView.getChildAt(i);
if (c.isChecked()) {
WallpaperTileInfo info = (WallpaperTileInfo) c.getTag();
info.onDelete(WallpaperPickerActivity.this);
viewsToRemove.add(c);
if (i == mSelectedIndex) {
selectedTileRemoved = true;
}
}
}
for (View v : viewsToRemove) {
mWallpapersView.removeView(v);
}
if (selectedTileRemoved) {
mSelectedIndex = -1;
mSelectedTile = null;
setSystemWallpaperVisiblity(true);
}
updateTileIndices();
mode.finish(); // Action picked, so close the CAB
return true;
} else {
return false;
}
}
/**
* Called when the user exits the action mode
*/
@Override
public void onDestroyActionMode(ActionMode mode) {
int childCount = mWallpapersView.getChildCount();
for (int i = 0; i < childCount; i++) {
CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
c.setChecked(false);
}
if (mSelectedTile != null) {
mSelectedTile.setSelected(true);
}
mActionMode = null;
}
}