Rename Honeycomb-Gallery to HoneycombGallery (remove the dash).
Change-Id: I61000b27b03e0381e5e60b2cfdd79d8619451cba
This commit is contained in:
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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.example.android.hcgallery;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.app.Activity;
|
||||
import android.app.ActionBar;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.hardware.Camera;
|
||||
import android.hardware.Camera.CameraInfo;
|
||||
import android.hardware.Camera.Size;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
public class CameraFragment extends Fragment {
|
||||
|
||||
private Preview mPreview;
|
||||
Camera mCamera;
|
||||
int mNumberOfCameras;
|
||||
int mCameraCurrentlyLocked;
|
||||
|
||||
// The first rear facing camera
|
||||
int mDefaultCameraId;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
|
||||
// Create a RelativeLayout container that will hold a SurfaceView,
|
||||
// and set it as the content of our activity.
|
||||
mPreview = new Preview(this.getActivity());
|
||||
|
||||
// Find the total number of cameras available
|
||||
mNumberOfCameras = Camera.getNumberOfCameras();
|
||||
|
||||
// Find the ID of the default camera
|
||||
CameraInfo cameraInfo = new CameraInfo();
|
||||
for (int i = 0; i < mNumberOfCameras; i++) {
|
||||
Camera.getCameraInfo(i, cameraInfo);
|
||||
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
|
||||
mDefaultCameraId = i;
|
||||
}
|
||||
}
|
||||
setHasOptionsMenu(mNumberOfCameras > 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
// Add an up arrow to the "home" button, indicating that the button will go "up"
|
||||
// one activity in the app's Activity heirarchy.
|
||||
// Calls to getActionBar() aren't guaranteed to return the ActionBar when called
|
||||
// from within the Fragment's onCreate method, because the Window's decor hasn't been
|
||||
// initialized yet. Either call for the ActionBar reference in Activity.onCreate()
|
||||
// (after the setContentView(...) call), or in the Fragment's onActivityCreated method.
|
||||
Activity activity = this.getActivity();
|
||||
ActionBar actionBar = activity.getActionBar();
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return mPreview;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// Open the default i.e. the first rear facing camera.
|
||||
mCamera = Camera.open(mDefaultCameraId);
|
||||
mCameraCurrentlyLocked = mDefaultCameraId;
|
||||
mPreview.setCamera(mCamera);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
// Because the Camera object is a shared resource, it's very
|
||||
// important to release it when the activity is paused.
|
||||
if (mCamera != null) {
|
||||
mPreview.setCamera(null);
|
||||
mCamera.release();
|
||||
mCamera = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
if (mNumberOfCameras > 1) {
|
||||
// Inflate our menu which can gather user input for switching camera
|
||||
inflater.inflate(R.menu.camera_menu, menu);
|
||||
} else {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
// Handle item selection
|
||||
switch (item.getItemId()) {
|
||||
case R.id.switch_cam:
|
||||
// Release this camera -> mCameraCurrentlyLocked
|
||||
if (mCamera != null) {
|
||||
mCamera.stopPreview();
|
||||
mPreview.setCamera(null);
|
||||
mCamera.release();
|
||||
mCamera = null;
|
||||
}
|
||||
|
||||
// Acquire the next camera and request Preview to reconfigure
|
||||
// parameters.
|
||||
mCamera = Camera
|
||||
.open((mCameraCurrentlyLocked + 1) % mNumberOfCameras);
|
||||
mCameraCurrentlyLocked = (mCameraCurrentlyLocked + 1)
|
||||
% mNumberOfCameras;
|
||||
mPreview.switchCamera(mCamera);
|
||||
|
||||
// Start the preview
|
||||
mCamera.startPreview();
|
||||
return true;
|
||||
case android.R.id.home:
|
||||
Intent intent = new Intent(this.getActivity(), MainActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
startActivity(intent);
|
||||
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A simple wrapper around a Camera and a SurfaceView that renders a centered
|
||||
* preview of the Camera to the surface. We need to center the SurfaceView
|
||||
* because not all devices have cameras that support preview sizes at the same
|
||||
* aspect ratio as the device's display.
|
||||
*/
|
||||
class Preview extends ViewGroup implements SurfaceHolder.Callback {
|
||||
private final String TAG = "Preview";
|
||||
|
||||
SurfaceView mSurfaceView;
|
||||
SurfaceHolder mHolder;
|
||||
Size mPreviewSize;
|
||||
List<Size> mSupportedPreviewSizes;
|
||||
Camera mCamera;
|
||||
|
||||
Preview(Context context) {
|
||||
super(context);
|
||||
|
||||
mSurfaceView = new SurfaceView(context);
|
||||
addView(mSurfaceView);
|
||||
|
||||
// Install a SurfaceHolder.Callback so we get notified when the
|
||||
// underlying surface is created and destroyed.
|
||||
mHolder = mSurfaceView.getHolder();
|
||||
mHolder.addCallback(this);
|
||||
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
|
||||
}
|
||||
|
||||
public void setCamera(Camera camera) {
|
||||
mCamera = camera;
|
||||
if (mCamera != null) {
|
||||
mSupportedPreviewSizes = mCamera.getParameters()
|
||||
.getSupportedPreviewSizes();
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public void switchCamera(Camera camera) {
|
||||
setCamera(camera);
|
||||
try {
|
||||
camera.setPreviewDisplay(mHolder);
|
||||
} catch (IOException exception) {
|
||||
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
|
||||
}
|
||||
Camera.Parameters parameters = camera.getParameters();
|
||||
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
|
||||
requestLayout();
|
||||
|
||||
camera.setParameters(parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
// We purposely disregard child measurements because act as a
|
||||
// wrapper to a SurfaceView that centers the camera preview instead
|
||||
// of stretching it.
|
||||
final int width = resolveSize(getSuggestedMinimumWidth(),
|
||||
widthMeasureSpec);
|
||||
final int height = resolveSize(getSuggestedMinimumHeight(),
|
||||
heightMeasureSpec);
|
||||
setMeasuredDimension(width, height);
|
||||
|
||||
if (mSupportedPreviewSizes != null) {
|
||||
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width,
|
||||
height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
if (changed && getChildCount() > 0) {
|
||||
final View child = getChildAt(0);
|
||||
|
||||
final int width = r - l;
|
||||
final int height = b - t;
|
||||
|
||||
int previewWidth = width;
|
||||
int previewHeight = height;
|
||||
if (mPreviewSize != null) {
|
||||
previewWidth = mPreviewSize.width;
|
||||
previewHeight = mPreviewSize.height;
|
||||
}
|
||||
|
||||
// Center the child SurfaceView within the parent.
|
||||
if (width * previewHeight > height * previewWidth) {
|
||||
final int scaledChildWidth = previewWidth * height
|
||||
/ previewHeight;
|
||||
child.layout((width - scaledChildWidth) / 2, 0,
|
||||
(width + scaledChildWidth) / 2, height);
|
||||
} else {
|
||||
final int scaledChildHeight = previewHeight * width
|
||||
/ previewWidth;
|
||||
child.layout(0, (height - scaledChildHeight) / 2, width,
|
||||
(height + scaledChildHeight) / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
// The Surface has been created, acquire the camera and tell it where
|
||||
// to draw.
|
||||
try {
|
||||
if (mCamera != null) {
|
||||
mCamera.setPreviewDisplay(holder);
|
||||
}
|
||||
} catch (IOException exception) {
|
||||
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
|
||||
}
|
||||
}
|
||||
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
// Surface will be destroyed when we return, so stop the preview.
|
||||
if (mCamera != null) {
|
||||
mCamera.stopPreview();
|
||||
}
|
||||
}
|
||||
|
||||
private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
|
||||
final double ASPECT_TOLERANCE = 0.1;
|
||||
double targetRatio = (double) w / h;
|
||||
if (sizes == null)
|
||||
return null;
|
||||
|
||||
Size optimalSize = null;
|
||||
double minDiff = Double.MAX_VALUE;
|
||||
|
||||
int targetHeight = h;
|
||||
|
||||
// Try to find an size match aspect ratio and size
|
||||
for (Size size : sizes) {
|
||||
double ratio = (double) size.width / size.height;
|
||||
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
|
||||
continue;
|
||||
if (Math.abs(size.height - targetHeight) < minDiff) {
|
||||
optimalSize = size;
|
||||
minDiff = Math.abs(size.height - targetHeight);
|
||||
}
|
||||
}
|
||||
|
||||
// Cannot find the one match the aspect ratio, ignore the requirement
|
||||
if (optimalSize == null) {
|
||||
minDiff = Double.MAX_VALUE;
|
||||
for (Size size : sizes) {
|
||||
if (Math.abs(size.height - targetHeight) < minDiff) {
|
||||
optimalSize = size;
|
||||
minDiff = Math.abs(size.height - targetHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
return optimalSize;
|
||||
}
|
||||
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
|
||||
// Now that the size is known, set up the camera parameters and begin
|
||||
// the preview.
|
||||
Camera.Parameters parameters = mCamera.getParameters();
|
||||
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
|
||||
requestLayout();
|
||||
|
||||
mCamera.setParameters(parameters);
|
||||
mCamera.startPreview();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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.example.android.hcgallery;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class CameraSample extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
int themeId = this.getIntent().getExtras().getInt("theme");
|
||||
this.setTheme(themeId);
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.camera_sample);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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.example.android.hcgallery;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipData.Item;
|
||||
import android.content.ClipDescription;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.ActionMode;
|
||||
import android.view.DragEvent;
|
||||
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.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
public class ContentFragment extends Fragment {
|
||||
private View mContentView;
|
||||
|
||||
// The bitmap currently used by ImageView
|
||||
private Bitmap mBitmap = null;
|
||||
|
||||
// Current action mode (contextual action bar, a.k.a. CAB)
|
||||
private ActionMode mCurrentActionMode;
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
mContentView = inflater.inflate(R.layout.content_welcome, null);
|
||||
final ImageView imageView = (ImageView) mContentView.findViewById(R.id.image);
|
||||
mContentView.setDrawingCacheEnabled(false);
|
||||
|
||||
mContentView.setOnDragListener(new View.OnDragListener() {
|
||||
public boolean onDrag(View v, DragEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case DragEvent.ACTION_DRAG_ENTERED:
|
||||
mContentView.setBackgroundColor(
|
||||
getResources().getColor(R.color.drag_active_color));
|
||||
break;
|
||||
|
||||
case DragEvent.ACTION_DRAG_EXITED:
|
||||
mContentView.setBackgroundColor(Color.TRANSPARENT);
|
||||
break;
|
||||
|
||||
case DragEvent.ACTION_DRAG_STARTED:
|
||||
return processDragStarted(event);
|
||||
|
||||
case DragEvent.ACTION_DROP:
|
||||
mContentView.setBackgroundColor(Color.TRANSPARENT);
|
||||
return processDrop(event, imageView);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Keep the action bar visibility in sync with the system status bar. That is, when entering
|
||||
// 'lights out mode,' hide the action bar, and when exiting this mode, show the action bar.
|
||||
|
||||
final Activity activity = getActivity();
|
||||
mContentView.setOnSystemUiVisibilityChangeListener(
|
||||
new View.OnSystemUiVisibilityChangeListener() {
|
||||
public void onSystemUiVisibilityChange(int visibility) {
|
||||
ActionBar actionBar = activity.getActionBar();
|
||||
if (actionBar != null) {
|
||||
mContentView.setSystemUiVisibility(visibility);
|
||||
if (visibility == View.STATUS_BAR_VISIBLE) {
|
||||
actionBar.show();
|
||||
} else {
|
||||
actionBar.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Show/hide the system status bar when single-clicking a photo. This is also called
|
||||
// 'lights out mode.' Activating and deactivating this mode also invokes the listener
|
||||
// defined above, which will show or hide the action bar accordingly.
|
||||
|
||||
mContentView.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
if (mContentView.getSystemUiVisibility() == View.STATUS_BAR_VISIBLE) {
|
||||
mContentView.setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
|
||||
} else {
|
||||
mContentView.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// When long-pressing a photo, activate the action mode for selection, showing the
|
||||
// contextual action bar (CAB).
|
||||
|
||||
mContentView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
public boolean onLongClick(View view) {
|
||||
if (mCurrentActionMode != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mCurrentActionMode = getActivity().startActionMode(
|
||||
mContentSelectionActionModeCallback);
|
||||
mContentView.setSelected(true);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return mContentView;
|
||||
}
|
||||
|
||||
boolean processDragStarted(DragEvent event) {
|
||||
// Determine whether to continue processing drag and drop based on the
|
||||
// plain text mime type.
|
||||
ClipDescription clipDesc = event.getClipDescription();
|
||||
if (clipDesc != null) {
|
||||
return clipDesc.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean processDrop(DragEvent event, ImageView imageView) {
|
||||
// Attempt to parse clip data with expected format: category||entry_id.
|
||||
// Ignore event if data does not conform to this format.
|
||||
ClipData data = event.getClipData();
|
||||
if (data != null) {
|
||||
if (data.getItemCount() > 0) {
|
||||
Item item = data.getItemAt(0);
|
||||
String textData = (String) item.getText();
|
||||
if (textData != null) {
|
||||
StringTokenizer tokenizer = new StringTokenizer(textData, "||");
|
||||
if (tokenizer.countTokens() != 2) {
|
||||
return false;
|
||||
}
|
||||
int category = -1;
|
||||
int entryId = -1;
|
||||
try {
|
||||
category = Integer.parseInt(tokenizer.nextToken());
|
||||
entryId = Integer.parseInt(tokenizer.nextToken());
|
||||
} catch (NumberFormatException exception) {
|
||||
return false;
|
||||
}
|
||||
updateContentAndRecycleBitmap(category, entryId);
|
||||
// Update list fragment with selected entry.
|
||||
TitlesFragment titlesFrag = (TitlesFragment)
|
||||
getFragmentManager().findFragmentById(R.id.frag_title);
|
||||
titlesFrag.selectPosition(entryId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void updateContentAndRecycleBitmap(int category, int position) {
|
||||
if (mCurrentActionMode != null) {
|
||||
mCurrentActionMode.finish();
|
||||
}
|
||||
|
||||
if (mBitmap != null) {
|
||||
// This is an advanced call and should be used if you
|
||||
// are working with a lot of bitmaps. The bitmap is dead
|
||||
// after this call.
|
||||
mBitmap.recycle();
|
||||
}
|
||||
|
||||
// Get the bitmap that needs to be drawn and update the ImageView
|
||||
mBitmap = Directory.getCategory(category).getEntry(position)
|
||||
.getBitmap(getResources());
|
||||
((ImageView) getView().findViewById(R.id.image)).setImageBitmap(mBitmap);
|
||||
}
|
||||
|
||||
void shareCurrentPhoto() {
|
||||
File externalCacheDir = getActivity().getExternalCacheDir();
|
||||
if (externalCacheDir == null) {
|
||||
Toast.makeText(getActivity(), "Error writing to USB/external storage.",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent media scanning of the cache directory.
|
||||
final File noMediaFile = new File(externalCacheDir, ".nomedia");
|
||||
try {
|
||||
noMediaFile.createNewFile();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
|
||||
// Write the bitmap to temporary storage in the external storage directory (e.g. SD card).
|
||||
// We perform the actual disk write operations on a separate thread using the
|
||||
// {@link AsyncTask} class, thus avoiding the possibility of stalling the main (UI) thread.
|
||||
|
||||
final File tempFile = new File(externalCacheDir, "tempfile.jpg");
|
||||
|
||||
new AsyncTask<Void, Void, Boolean>() {
|
||||
/**
|
||||
* Compress and write the bitmap to disk on a separate thread.
|
||||
* @return TRUE if the write was successful, FALSE otherwise.
|
||||
*/
|
||||
protected Boolean doInBackground(Void... voids) {
|
||||
try {
|
||||
FileOutputStream fo = new FileOutputStream(tempFile, false);
|
||||
if (!mBitmap.compress(Bitmap.CompressFormat.JPEG, 60, fo)) {
|
||||
Toast.makeText(getActivity(), "Error writing bitmap data.",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
Toast.makeText(getActivity(), "Error writing to USB/external storage.",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* After doInBackground completes (either successfully or in failure), we invoke an
|
||||
* intent to share the photo. This code is run on the main (UI) thread.
|
||||
*/
|
||||
protected void onPostExecute(Boolean result) {
|
||||
if (result != Boolean.TRUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(tempFile));
|
||||
shareIntent.setType("image/jpeg");
|
||||
startActivity(Intent.createChooser(shareIntent, "Share photo"));
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* The callback for the 'photo selected' {@link ActionMode}. In this action mode, we can
|
||||
* provide contextual actions for the selected photo. We currently only provide the 'share'
|
||||
* action, but we could also add clipboard functions such as cut/copy/paste here as well.
|
||||
*/
|
||||
private ActionMode.Callback mContentSelectionActionModeCallback = new ActionMode.Callback() {
|
||||
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
|
||||
actionMode.setTitle(R.string.photo_selection_cab_title);
|
||||
|
||||
MenuInflater inflater = getActivity().getMenuInflater();
|
||||
inflater.inflate(R.menu.photo_context_menu, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
|
||||
switch (menuItem.getItemId()) {
|
||||
case R.id.share:
|
||||
shareCurrentPhoto();
|
||||
actionMode.finish();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onDestroyActionMode(ActionMode actionMode) {
|
||||
mContentView.setSelected(false);
|
||||
mCurrentActionMode = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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.example.android.hcgallery;
|
||||
|
||||
public class Directory {
|
||||
private static DirectoryCategory[] mCategories;
|
||||
|
||||
public static void initializeDirectory() {
|
||||
mCategories = new DirectoryCategory[] {
|
||||
new DirectoryCategory("Balloons", new DirectoryEntry[] {
|
||||
new DirectoryEntry("Red Balloon", R.drawable.red_balloon),
|
||||
new DirectoryEntry("Green Balloon", R.drawable.green_balloon),
|
||||
new DirectoryEntry("Blue Balloon", R.drawable.blue_balloon)}),
|
||||
new DirectoryCategory("Bikes", new DirectoryEntry[] {
|
||||
new DirectoryEntry("Old school huffy", R.drawable.blue_bike),
|
||||
new DirectoryEntry("New Bikes", R.drawable.rainbow_bike),
|
||||
new DirectoryEntry("Chrome Fast", R.drawable.chrome_wheel)}),
|
||||
new DirectoryCategory("Androids", new DirectoryEntry[] {
|
||||
new DirectoryEntry("Steampunk Android", R.drawable.punk_droid),
|
||||
new DirectoryEntry("Stargazing Android", R.drawable.stargazer_droid),
|
||||
new DirectoryEntry("Big Android", R.drawable.big_droid) }),
|
||||
new DirectoryCategory("Pastries", new DirectoryEntry[] {
|
||||
new DirectoryEntry("Cupcake", R.drawable.cupcake),
|
||||
new DirectoryEntry("Donut", R.drawable.donut),
|
||||
new DirectoryEntry("Eclair", R.drawable.eclair),
|
||||
new DirectoryEntry("Froyo", R.drawable.froyo), }), };
|
||||
|
||||
}
|
||||
|
||||
public static int getCategoryCount() {
|
||||
return mCategories.length;
|
||||
}
|
||||
|
||||
public static DirectoryCategory getCategory(int i) {
|
||||
return mCategories[i];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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.example.android.hcgallery;
|
||||
|
||||
public class DirectoryCategory {
|
||||
private String name;
|
||||
private DirectoryEntry[] entries;
|
||||
|
||||
public DirectoryCategory(String name, DirectoryEntry[] entries) {
|
||||
this.name = name;
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getEntryCount() {
|
||||
return entries.length;
|
||||
}
|
||||
|
||||
public DirectoryEntry getEntry(int i) {
|
||||
return entries[i];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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.example.android.hcgallery;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
public class DirectoryEntry {
|
||||
private String name;
|
||||
private int resID;
|
||||
|
||||
public DirectoryEntry(String name, int resID) {
|
||||
this.name = name;
|
||||
this.resID = resID;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Drawable getDrawable(Resources res) {
|
||||
return res.getDrawable(resID);
|
||||
}
|
||||
|
||||
public Bitmap getBitmap(Resources res) {
|
||||
return BitmapFactory.decodeResource(res, resID);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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.example.android.hcgallery;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* A simple layout that fits and centers each child view, maintaining aspect ratio.
|
||||
*/
|
||||
public class FitCenterFrameLayout extends ViewGroup {
|
||||
public FitCenterFrameLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public FitCenterFrameLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
// We purposely disregard child measurements.
|
||||
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
|
||||
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
|
||||
setMeasuredDimension(width, height);
|
||||
|
||||
int childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED);
|
||||
int childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.UNSPECIFIED);
|
||||
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
getChildAt(i).measure(childWidthSpec, childHeightSpec);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
final int childCount = getChildCount();
|
||||
|
||||
final int parentLeft = getPaddingLeft();
|
||||
final int parentTop = getPaddingTop();
|
||||
final int parentRight = r - l - getPaddingRight();
|
||||
final int parentBottom = b - t - getPaddingBottom();
|
||||
|
||||
final int parentWidth = parentRight - parentLeft;
|
||||
final int parentHeight = parentBottom - parentTop;
|
||||
|
||||
int unpaddedWidth, unpaddedHeight, parentUnpaddedWidth, parentUnpaddedHeight;
|
||||
int childPaddingLeft, childPaddingTop, childPaddingRight, childPaddingBottom;
|
||||
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = getChildAt(i);
|
||||
if (child.getVisibility() == GONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Fit and center the child within the parent. Make sure not to consider padding
|
||||
// as part of the child's aspect ratio.
|
||||
|
||||
childPaddingLeft = child.getPaddingLeft();
|
||||
childPaddingTop = child.getPaddingTop();
|
||||
childPaddingRight = child.getPaddingRight();
|
||||
childPaddingBottom = child.getPaddingBottom();
|
||||
|
||||
unpaddedWidth = child.getMeasuredWidth() - childPaddingLeft - childPaddingRight;
|
||||
unpaddedHeight = child.getMeasuredHeight() - childPaddingTop - childPaddingBottom;
|
||||
|
||||
parentUnpaddedWidth = parentWidth - childPaddingLeft - childPaddingRight;
|
||||
parentUnpaddedHeight = parentHeight - childPaddingTop - childPaddingBottom;
|
||||
|
||||
if (parentUnpaddedWidth * unpaddedHeight > parentUnpaddedHeight * unpaddedWidth) {
|
||||
// The child view should be left/right letterboxed.
|
||||
final int scaledChildWidth = unpaddedWidth * parentUnpaddedHeight
|
||||
/ unpaddedHeight + childPaddingLeft + childPaddingRight;
|
||||
child.layout(
|
||||
parentLeft + (parentWidth - scaledChildWidth) / 2,
|
||||
parentTop,
|
||||
parentRight - (parentWidth - scaledChildWidth) / 2,
|
||||
parentBottom);
|
||||
} else {
|
||||
// The child view should be top/bottom letterboxed.
|
||||
final int scaledChildHeight = unpaddedHeight * parentUnpaddedWidth
|
||||
/ unpaddedWidth + childPaddingTop + childPaddingBottom;
|
||||
child.layout(
|
||||
parentLeft,
|
||||
parentTop + (parentHeight - scaledChildHeight) / 2,
|
||||
parentRight,
|
||||
parentTop + (parentHeight + scaledChildHeight) / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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.example.android.hcgallery;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
public class MainActivity extends Activity implements ActionBar.TabListener {
|
||||
|
||||
private static final int NOTIFICATION_DEFAULT = 1;
|
||||
private static final String ACTION_DIALOG = "com.example.android.hcgallery.action.DIALOG";
|
||||
|
||||
private View mActionBarView;
|
||||
private Animator mCurrentTitlesAnimator;
|
||||
private String[] mToggleLabels = {"Show Titles", "Hide Titles"};
|
||||
private int mLabelIndex = 1;
|
||||
private int mThemeId = -1;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if(savedInstanceState != null && savedInstanceState.getInt("theme", -1) != -1) {
|
||||
mThemeId = savedInstanceState.getInt("theme");
|
||||
this.setTheme(mThemeId);
|
||||
}
|
||||
|
||||
setContentView(R.layout.main);
|
||||
|
||||
Directory.initializeDirectory();
|
||||
|
||||
ActionBar bar = getActionBar();
|
||||
|
||||
int i;
|
||||
for (i = 0; i < Directory.getCategoryCount(); i++) {
|
||||
bar.addTab(bar.newTab().setText(Directory.getCategory(i).getName())
|
||||
.setTabListener(this));
|
||||
}
|
||||
|
||||
mActionBarView = getLayoutInflater().inflate(
|
||||
R.layout.action_bar_custom, null);
|
||||
|
||||
bar.setCustomView(mActionBarView);
|
||||
bar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_USE_LOGO);
|
||||
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
|
||||
bar.setDisplayShowHomeEnabled(true);
|
||||
|
||||
// If category is not saved to the savedInstanceState,
|
||||
// 0 is returned by default.
|
||||
if(savedInstanceState != null) {
|
||||
int category = savedInstanceState.getInt("category");
|
||||
bar.selectTab(bar.getTabAt(category));
|
||||
}
|
||||
}
|
||||
|
||||
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
|
||||
TitlesFragment titleFrag = (TitlesFragment) getFragmentManager()
|
||||
.findFragmentById(R.id.frag_title);
|
||||
titleFrag.populateTitles(tab.getPosition());
|
||||
|
||||
titleFrag.selectPosition(0);
|
||||
}
|
||||
|
||||
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
|
||||
}
|
||||
|
||||
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.main_menu, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.camera:
|
||||
Intent intent = new Intent(this, CameraSample.class);
|
||||
intent.putExtra("theme", mThemeId);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
|
||||
case R.id.toggleTitles:
|
||||
toggleVisibleTitles();
|
||||
return true;
|
||||
|
||||
case R.id.toggleTheme:
|
||||
if (mThemeId == R.style.AppTheme_Dark) {
|
||||
mThemeId = R.style.AppTheme_Light;
|
||||
} else {
|
||||
mThemeId = R.style.AppTheme_Dark;
|
||||
}
|
||||
this.recreate();
|
||||
return true;
|
||||
|
||||
case R.id.showDialog:
|
||||
showDialog("This is indeed an awesome dialog.");
|
||||
return true;
|
||||
|
||||
case R.id.showStandardNotification:
|
||||
showNotification(false);
|
||||
return true;
|
||||
|
||||
case R.id.showCustomNotification:
|
||||
showNotification(true);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void toggleVisibleTitles() {
|
||||
// Use these for custom animations.
|
||||
final FragmentManager fm = getFragmentManager();
|
||||
final TitlesFragment f = (TitlesFragment) fm
|
||||
.findFragmentById(R.id.frag_title);
|
||||
final View titlesView = f.getView();
|
||||
mLabelIndex = 1 - mLabelIndex;
|
||||
|
||||
// Determine if we're in portrait, and whether we're showing or hiding the titles
|
||||
// with this toggle.
|
||||
final boolean isPortrait = getResources().getConfiguration().orientation ==
|
||||
Configuration.ORIENTATION_PORTRAIT;
|
||||
|
||||
final boolean shouldShow = f.isHidden() || mCurrentTitlesAnimator != null;
|
||||
|
||||
// Cancel the current titles animation if there is one.
|
||||
if (mCurrentTitlesAnimator != null)
|
||||
mCurrentTitlesAnimator.cancel();
|
||||
|
||||
// Begin setting up the object animator. We'll animate the bottom or right edge of the
|
||||
// titles view, as well as its alpha for a fade effect.
|
||||
ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(
|
||||
titlesView,
|
||||
PropertyValuesHolder.ofInt(
|
||||
isPortrait ? "bottom" : "right",
|
||||
shouldShow ? getResources().getDimensionPixelSize(R.dimen.titles_size)
|
||||
: 0),
|
||||
PropertyValuesHolder.ofFloat("alpha", shouldShow ? 1 : 0)
|
||||
);
|
||||
|
||||
// At each step of the animation, we'll perform layout by calling setLayoutParams.
|
||||
final ViewGroup.LayoutParams lp = titlesView.getLayoutParams();
|
||||
objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
public void onAnimationUpdate(ValueAnimator valueAnimator) {
|
||||
// *** WARNING ***: triggering layout at each animation frame highly impacts
|
||||
// performance so you should only do this for simple layouts. More complicated
|
||||
// layouts can be better served with individual animations on child views to
|
||||
// avoid the performance penalty of layout.
|
||||
if (isPortrait) {
|
||||
lp.height = (Integer) valueAnimator.getAnimatedValue();
|
||||
} else {
|
||||
lp.width = (Integer) valueAnimator.getAnimatedValue();
|
||||
}
|
||||
titlesView.setLayoutParams(lp);
|
||||
}
|
||||
});
|
||||
|
||||
if (shouldShow) {
|
||||
fm.beginTransaction().show(f).commit();
|
||||
objectAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
mCurrentTitlesAnimator = null;
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
objectAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
boolean canceled;
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
canceled = true;
|
||||
super.onAnimationCancel(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
if (canceled)
|
||||
return;
|
||||
mCurrentTitlesAnimator = null;
|
||||
fm.beginTransaction().hide(f).commit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Start the animation.
|
||||
objectAnimator.start();
|
||||
mCurrentTitlesAnimator = objectAnimator;
|
||||
|
||||
invalidateOptionsMenu();
|
||||
|
||||
// Manually trigger onNewIntent to check for ACTION_DIALOG.
|
||||
onNewIntent(getIntent());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
if (ACTION_DIALOG.equals(intent.getAction())) {
|
||||
showDialog(intent.getStringExtra(Intent.EXTRA_TEXT));
|
||||
}
|
||||
}
|
||||
|
||||
void showDialog(String text) {
|
||||
// DialogFragment.show() will take care of adding the fragment
|
||||
// in a transaction. We also want to remove any currently showing
|
||||
// dialog, so make our own transaction and take care of that here.
|
||||
FragmentTransaction ft = getFragmentManager().beginTransaction();
|
||||
|
||||
DialogFragment newFragment = MyDialogFragment.newInstance(text);
|
||||
|
||||
// Show the dialog.
|
||||
newFragment.show(ft, "dialog");
|
||||
}
|
||||
|
||||
void showNotification(boolean custom) {
|
||||
final Resources res = getResources();
|
||||
final NotificationManager notificationManager = (NotificationManager) getSystemService(
|
||||
NOTIFICATION_SERVICE);
|
||||
|
||||
Notification.Builder builder = new Notification.Builder(this)
|
||||
.setSmallIcon(R.drawable.ic_stat_notify_example)
|
||||
.setAutoCancel(true)
|
||||
.setTicker(getString(R.string.notification_text))
|
||||
.setContentIntent(getDialogPendingIntent("Tapped the notification entry."));
|
||||
|
||||
if (custom) {
|
||||
// Sets a custom content view for the notification, including an image button.
|
||||
RemoteViews layout = new RemoteViews(getPackageName(), R.layout.notification);
|
||||
layout.setTextViewText(R.id.notification_title, getString(R.string.app_name));
|
||||
layout.setOnClickPendingIntent(R.id.notification_button,
|
||||
getDialogPendingIntent("Tapped the 'dialog' button in the notification."));
|
||||
builder.setContent(layout);
|
||||
|
||||
// Notifications in Android 3.0 now have a standard mechanism for displaying large
|
||||
// bitmaps such as contact avatars. Here, we load an example image and resize it to the
|
||||
// appropriate size for large bitmaps in notifications.
|
||||
Bitmap largeIconTemp = BitmapFactory.decodeResource(res,
|
||||
R.drawable.notification_default_largeicon);
|
||||
Bitmap largeIcon = Bitmap.createScaledBitmap(
|
||||
largeIconTemp,
|
||||
res.getDimensionPixelSize(android.R.dimen.notification_large_icon_width),
|
||||
res.getDimensionPixelSize(android.R.dimen.notification_large_icon_height),
|
||||
false);
|
||||
largeIconTemp.recycle();
|
||||
|
||||
builder.setLargeIcon(largeIcon);
|
||||
|
||||
} else {
|
||||
builder
|
||||
.setNumber(7) // An example number.
|
||||
.setContentTitle(getString(R.string.app_name))
|
||||
.setContentText(getString(R.string.notification_text));
|
||||
}
|
||||
|
||||
notificationManager.notify(NOTIFICATION_DEFAULT, builder.getNotification());
|
||||
}
|
||||
|
||||
PendingIntent getDialogPendingIntent(String dialogText) {
|
||||
return PendingIntent.getActivity(
|
||||
this,
|
||||
dialogText.hashCode(), // Otherwise previous PendingIntents with the same
|
||||
// requestCode may be overwritten.
|
||||
new Intent(ACTION_DIALOG)
|
||||
.putExtra(Intent.EXTRA_TEXT, dialogText)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
|
||||
0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
menu.getItem(1).setTitle(mToggleLabels[mLabelIndex]);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState (Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
ActionBar bar = getActionBar();
|
||||
int category = bar.getSelectedTab().getPosition();
|
||||
outState.putInt("category", category);
|
||||
outState.putInt("theme", mThemeId);
|
||||
}
|
||||
|
||||
|
||||
public static class MyDialogFragment extends DialogFragment {
|
||||
|
||||
public static MyDialogFragment newInstance(String title) {
|
||||
MyDialogFragment frag = new MyDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString("text", title);
|
||||
frag.setArguments(args);
|
||||
return frag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
String text = getArguments().getString("text");
|
||||
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setTitle("A Dialog of Awesome")
|
||||
.setMessage(text)
|
||||
.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int whichButton) {
|
||||
}
|
||||
}
|
||||
)
|
||||
.create();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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.example.android.hcgallery;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.ListFragment;
|
||||
import android.content.ClipData;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextPaint;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.AdapterView.OnItemLongClickListener;
|
||||
|
||||
public class TitlesFragment extends ListFragment {
|
||||
private int mCategory = 0;
|
||||
private int mCurPosition = 0;
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
//Current position should survive screen rotations.
|
||||
if (savedInstanceState != null) {
|
||||
mCategory = savedInstanceState.getInt("category");
|
||||
mCurPosition = savedInstanceState.getInt("listPosition");
|
||||
}
|
||||
|
||||
populateTitles(mCategory);
|
||||
ListView lv = getListView();
|
||||
lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
||||
lv.setCacheColorHint(Color.TRANSPARENT);
|
||||
lv.setOnItemLongClickListener(new OnItemLongClickListener() {
|
||||
public boolean onItemLongClick(AdapterView<?> av, View v, int pos, long id) {
|
||||
final String title = (String) ((TextView) v).getText();
|
||||
|
||||
// Set up clip data with the category||entry_id format.
|
||||
final String textData = String.format("%d||%d", mCategory, pos);
|
||||
ClipData data = ClipData.newPlainText(title, textData);
|
||||
v.startDrag(data, new MyDragShadowBuilder(v), null, 0);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
selectPosition(mCurPosition);
|
||||
}
|
||||
|
||||
private class MyDragShadowBuilder extends View.DragShadowBuilder {
|
||||
private Drawable mShadow;
|
||||
|
||||
public MyDragShadowBuilder(View v) {
|
||||
super(v);
|
||||
|
||||
final TypedArray a = v.getContext().obtainStyledAttributes(R.styleable.AppTheme);
|
||||
mShadow = a.getDrawable(R.styleable.AppTheme_listDragShadowBackground);
|
||||
mShadow.setCallback(v);
|
||||
mShadow.setBounds(0, 0, v.getWidth(), v.getHeight());
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawShadow(Canvas canvas) {
|
||||
super.onDrawShadow(canvas);
|
||||
mShadow.draw(canvas);
|
||||
getView().draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
public void populateTitles(int category) {
|
||||
DirectoryCategory cat = Directory.getCategory(category);
|
||||
String[] items = new String[cat.getEntryCount()];
|
||||
for (int i = 0; i < cat.getEntryCount(); i++)
|
||||
items[i] = cat.getEntry(i).getName();
|
||||
setListAdapter(new ArrayAdapter<String>(getActivity(),
|
||||
R.layout.title_list_item, items));
|
||||
mCategory = category;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
||||
updateImage(position);
|
||||
}
|
||||
|
||||
private void updateImage(int position) {
|
||||
ContentFragment frag = (ContentFragment) getFragmentManager()
|
||||
.findFragmentById(R.id.frag_content);
|
||||
frag.updateContentAndRecycleBitmap(mCategory, position);
|
||||
mCurPosition = position;
|
||||
}
|
||||
|
||||
public void selectPosition(int position) {
|
||||
ListView lv = getListView();
|
||||
lv.setItemChecked(position, true);
|
||||
updateImage(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState (Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt("listPosition", mCurPosition);
|
||||
outState.putInt("category", mCategory);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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.example.android.hcgallery.widget;
|
||||
|
||||
public class WidgetItem {
|
||||
public String text;
|
||||
|
||||
public WidgetItem(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.example.android.hcgallery.widget;
|
||||
|
||||
import com.example.android.hcgallery.R;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class WidgetProvider extends AppWidgetProvider {
|
||||
public static String TOAST_ACTION = "com.example.android.widget.action.TOAST";
|
||||
|
||||
@Override
|
||||
public void onDeleted(Context context, int[] appWidgetIds) {
|
||||
super.onDeleted(context, appWidgetIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled(Context context) {
|
||||
super.onDisabled(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnabled(Context context) {
|
||||
super.onEnabled(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
|
||||
AppWidgetManager mgr = AppWidgetManager.getInstance(context);
|
||||
if (intent.getAction().equals(TOAST_ACTION)) {
|
||||
int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
|
||||
AppWidgetManager.INVALID_APPWIDGET_ID);
|
||||
int viewIndex = intent.getIntExtra("numberToToast", 0);
|
||||
Toast.makeText(context, "Touched view " + viewIndex, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
super.onReceive(context, intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||
// update each of the widgets with the remote adapter
|
||||
for (int i = 0; i < appWidgetIds.length; ++i) {
|
||||
|
||||
// Here we setup the intent which points to the StackViewService which will
|
||||
// provide the views for this collection.
|
||||
Intent intent = new Intent(context, WidgetService.class);
|
||||
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
|
||||
// When intents are compared, the extras are ignored, so we need to embed the extras
|
||||
// into the data so that the extras will not be ignored.
|
||||
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
|
||||
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
|
||||
rv.setRemoteAdapter(appWidgetIds[i], R.id.stack_view, intent);
|
||||
|
||||
// The empty view is displayed when the collection has no items. It should be a sibling
|
||||
// of the collection view.
|
||||
rv.setEmptyView(R.id.stack_view, R.id.empty_view);
|
||||
|
||||
// Here we setup the a pending intent template. Individuals items of a collection
|
||||
// cannot setup their own pending intents, instead, the collection as a whole can
|
||||
// setup a pending intent template, and the individual items can set a fillInIntent
|
||||
// to create unique before on an item to item basis.
|
||||
Intent toastIntent = new Intent(context, WidgetProvider.class);
|
||||
toastIntent.setAction(WidgetProvider.TOAST_ACTION);
|
||||
toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
|
||||
toastIntent.setData(Uri.parse("widgetid" + appWidgetIds[i]));
|
||||
PendingIntent toastPendingIntent = PendingIntent.getBroadcast(context, 0, toastIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
rv.setPendingIntentTemplate(R.id.stack_view, toastPendingIntent);
|
||||
|
||||
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
|
||||
}
|
||||
super.onUpdate(context, appWidgetManager, appWidgetIds);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.example.android.hcgallery.widget;
|
||||
|
||||
import com.example.android.hcgallery.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.RemoteViewsService;
|
||||
|
||||
public class WidgetService extends RemoteViewsService {
|
||||
|
||||
private class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
|
||||
private static final int mCount = 10;
|
||||
private List<WidgetItem> mWidgetItems = new ArrayList<WidgetItem>();
|
||||
private Context mContext;
|
||||
private int mAppWidgetId;
|
||||
|
||||
public StackRemoteViewsFactory(Context context, Intent intent) {
|
||||
mContext = context;
|
||||
mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
|
||||
AppWidgetManager.INVALID_APPWIDGET_ID);
|
||||
}
|
||||
|
||||
public void onCreate() {
|
||||
// In onCreate() you setup any connections / cursors to your data source. Heavy lifting,
|
||||
// for example downloading or creating content etc, should be deferred to getViewAt() or
|
||||
// onDataSetChanged(). Taking more than 20 seconds in this call will result in an ANR.
|
||||
for (int i = 0; i < mCount; i++) {
|
||||
mWidgetItems.add(new WidgetItem(i + "!"));
|
||||
}
|
||||
|
||||
// We sleep for 3 seconds here to show how the empty view appears in the interim.
|
||||
// The empty view is set in the WidgetProvider and should be a sibling of the
|
||||
// collection view.
|
||||
try {
|
||||
Thread.sleep(3000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
// In onDestroy() you should tear down anything that was setup for your data source,
|
||||
// eg. cursors, connections, etc.
|
||||
mWidgetItems.clear();
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return mCount;
|
||||
}
|
||||
|
||||
public RemoteViews getViewAt(int position) {
|
||||
// position will always range from 0 to getCount() - 1.
|
||||
|
||||
// We construct a remote views item based on our widget item xml file, and set the
|
||||
// text based on the position.
|
||||
RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item);
|
||||
rv.setTextViewText(R.id.widget_item, mWidgetItems.get(position).text);
|
||||
|
||||
// Next, we set an intent so that clicking on this view will result in a toast message
|
||||
Bundle extras = new Bundle();
|
||||
extras.putInt(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
|
||||
extras.putInt("numberToToast", position);
|
||||
Intent fillInIntent = new Intent();
|
||||
fillInIntent.putExtras(extras);
|
||||
rv.setOnClickFillInIntent(R.id.widget_item, fillInIntent);
|
||||
|
||||
// You can do heaving lifting in here, synchronously. For example, if you need to
|
||||
// process an image, fetch something from the network, etc., it is ok to do it here,
|
||||
// synchronously. A loading view will show up in lieu of the actual contents in the
|
||||
// interim.
|
||||
try {
|
||||
Log.d("WidgetService/getViewAt", "Loading view " + position);
|
||||
// Simulating a time-consuming operation. NO NEED to include this call in your app!
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Return our remote views object.
|
||||
return rv;
|
||||
}
|
||||
|
||||
public RemoteViews getLoadingView() {
|
||||
// You can create a custom loading view (for instance when getViewAt() is slow. If you
|
||||
// return null here, you will get the default loading view.
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getViewTypeCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onDataSetChanged() {
|
||||
// This is triggered when you call AppWidgetManager notifyAppWidgetViewDataChanged
|
||||
// on the collection view corresponding to this factory. You can do heaving lifting in
|
||||
// here, synchronously. For example, if you need to process an image, fetch something
|
||||
// from the network, etc., it is ok to do it here, synchronously. The widget will remain
|
||||
// in its current state while work is being done here, so you don't need to worry about
|
||||
// locking up the widget.
|
||||
}
|
||||
}
|
||||
|
||||
public RemoteViewsFactory onGetViewFactory(Intent intent) {
|
||||
return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user