Major update to HoneycombGallery

* Add action bar and icon theming
* Add contextual action bar and photo sharing
* Add notifications (with large icons too)
* Add fullscreening (lights-out mode)
* Add a picture frame

Change-Id: I07a1da435d5adc7bc76c7ac2cc087ff2efd74156
This commit is contained in:
Roman Nurik
2011-02-04 15:27:25 -08:00
parent 821736e1fb
commit f4f345d0b7
48 changed files with 668 additions and 112 deletions

View File

@@ -162,7 +162,7 @@ development/samples/ContactManager samples/${PLATFORM_NAME}/ContactMan
development/samples/CrossCompatibility samples/${PLATFORM_NAME}/CrossCompatibility
development/samples/CubeLiveWallpaper samples/${PLATFORM_NAME}/CubeLiveWallpaper
development/samples/Home samples/${PLATFORM_NAME}/Home
development/samples/Honeycomb-Gallery samples/${PLATFORM_NAME}/Honeycomb-Gallery
development/samples/HoneycombGallery samples/${PLATFORM_NAME}/HoneycombGallery
development/samples/JetBoy samples/${PLATFORM_NAME}/JetBoy
development/samples/LunarLander samples/${PLATFORM_NAME}/LunarLander
development/samples/MultiResolution samples/${PLATFORM_NAME}/MultiResolution

View File

@@ -6,7 +6,7 @@ LOCAL_MODULE_TAGS := samples
# Only compile source java files in this apk.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := Honeycomb-Gallery
LOCAL_PACKAGE_NAME := HoneycombGallery
LOCAL_SDK_VERSION := current

View File

@@ -19,20 +19,28 @@
android:versionName="1.0">
<uses-sdk android:minSdkVersion="11" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application android:label="@string/app_name"
android:icon="@drawable/icon"
android:logo="@drawable/logo"
android:theme="@android:style/Theme.Holo.Light"
android:theme="@style/AppTheme.Light"
android:hardwareAccelerated="true"
android:debuggable="true">
<activity android:name=".MainActivity" android:label="@string/app_name">
<activity android:name=".MainActivity"
android:label="@string/app_name"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="com.example.android.hcgallery.action.DIALOG" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<!-- CameraSample -->

View File

@@ -1,13 +1,27 @@
<p>This is a demo application highlighting how to use some of the new APIs in
Honeycomb, including Fragments, the Action Bar, drag'n drop, transition
animations, and a stack widget. The image gallery shows how all these pieces
can work together in one application.</p>
Honeycomb, including:</p>
<p>The application includes the following classes:<p>
<ul>
<li><a href="../../../guide/topics/fundamentals/fragments.html">Fragments</a></li>
<li>The <a href="../../../guide/topics/ui/actionbar.html">action bar</a>
and contextual action bar</li>
<li>Drag and drop</li>
<li>The new <a href="../../../reference/android/animation/package-summary.html"><code>android.animation</code></a>
framework</li>
<li>Custom notifications</li>
<li><a href="../../../reference/android/widget/StackView.html"><code>StackView</code></a>
and other adapter-based app widgets</li>
</ul>
<p>The image gallery shows how all these pieces can work together in one application.</p>
<p>The application includes the following key classes:<p>
<ul>
<li><a href="src/com/example/android/hcgallery/ContentFragment.html">ContentFragment</a>
A fragment responsible for containing the "content" of the application.
Displays images, receives drag/drop events from other fragments.</li>
A fragment responsible for containing the "content" of the application.
Displays images, receives drag/drop events from other fragments, and can
invoke the contextual action bar using
<a href="../../../reference/android/view/ActionMode.html">action modes</a>.</li>
<li><a href="src/com/example/android/hcgallery/TitlesFragment.html">TitlesFragment</a>
Shows a ListView of photos to display in the ContentFragment. Photos can
be chosen either by tapping on the listview, or dragging them from the
@@ -19,7 +33,7 @@ can work together in one application.</p>
ContentFragment. MainActivity is also responsible for keeping track of
the currently selected theme and currently selected photo when the
activity is recreated, such as when the screen is rotated or an intent to
a seperate activity is fired (such as the included Camera sample).
a separate activity is fired (such as the included Camera sample).
MainActivity also contains code demonstrating how to animate
showing/hiding fragments (in this case, the TitlesFragment) and the
ActionBar, demonstrating how to smoothly transition between states

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1013 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/btn_notification_ic_example_pressed" />
<item android:drawable="@drawable/btn_notification_ic_example_default" />
</selector>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="@drawable/picture_frame_selected" />
<item android:state_pressed="true" android:drawable="@drawable/picture_frame_pressed" />
<item android:drawable="@drawable/picture_frame_default" />
</selector>

View File

@@ -15,13 +15,16 @@
limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<view xmlns:android="http://schemas.android.com/apk/res/android"
class="com.example.android.hcgallery.FitCenterFrameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="12dp">
android:padding="24dp"
android:clickable="true">
<ImageView android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:layout_gravity="center" />
</FrameLayout>
android:background="@drawable/picture_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:duplicateParentState="true"/>
</view>

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/notificationbg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<LinearLayout
android:paddingLeft="16dip"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:orientation="vertical">
<TextView android:id="@+id/notification_title"
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
android:focusable="true"
android:ellipsize="marquee"
android:singleLine="true"
android:layout_gravity="left"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView android:id="@+id/notification_subtitle"
style="@android:style/TextAppearance.StatusBar.EventContent"
android:layout_gravity="left"
android:maxLines="2"
android:scrollHorizontally="true"
android:ellipsize="end"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/notification_text" />
</LinearLayout>
<ImageButton
android:id="@+id/notification_button"
android:src="@drawable/btn_notification_ic_example"
android:background="@null"
android:layout_weight="0"
android:layout_width="48dip"
android:layout_height="match_parent" />
</LinearLayout>

View File

@@ -18,16 +18,23 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/camera"
android:title="Camera"
android:icon="@drawable/ic_menu_camera"
android:icon="?attr/menuIconCamera"
android:showAsAction="ifRoom" />
<item android:id="@+id/toggleTitles"
android:icon="@drawable/ic_menu_toggle"
android:icon="?attr/menuIconToggle"
android:title="Toggle Titles"
android:showAsAction="ifRoom|withText" />
<!-- Example of items in the overflow menu -->
<item android:id="@+id/toggleTheme"
android:title="Day/Night"
android:showAsAction="ifRoom|withText" />
android:showAsAction="never" />
<item android:id="@+id/showDialog"
android:title="Show a dialog"
android:showAsAction="ifRoom|withText" />
android:title="Show a dialog"
android:showAsAction="never" />
<item android:id="@+id/showStandardNotification"
android:title="Show a basic notification"
android:showAsAction="never" />
<item android:id="@+id/showCustomNotification"
android:title="Show a custom notification"
android:showAsAction="never" />
</menu>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/share"
android:title="Share"
android:icon="?attr/menuIconShare"
android:showAsAction="always|withText" />
</menu>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<resources>
<declare-styleable name="AppTheme">
<attr name="listDragShadowBackground" format="reference" />
<attr name="menuIconCamera" format="reference" />
<attr name="menuIconToggle" format="reference" />
<attr name="menuIconShare" format="reference" />
</declare-styleable>
</resources>

View File

@@ -17,4 +17,5 @@
<resources>
<color name="actionbar_background_light">#ccffffff</color>
<color name="actionbar_background_dark">#cc000000</color>
<color name="drag_active_color">#80cccccc</color>
</resources>

View File

@@ -15,10 +15,14 @@
limitations under the License.
-->
<resources>
<string name="app_name">Honeycomb Example</string>
<string name="app_name">Honeycomb Gallery</string>
<string name="camera_sample">Camera Example</string>
<string name="clip_label">Clip Label</string>
<string name="app_widget_name">Honeycomb Example Widget</string>
<string name="widget_empty_view_text">Touch to show data</string>
<string name="notification_text">Example notification text</string>
<string name="photo_selection_cab_title">Photo selection</string>
</resources>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<resources>
<style name="ActionBar" parent="@android:style/Widget.Holo.ActionBar" />
<style name="ActionBar.Light" parent="@style/ActionBar">
<item name="android:background">@color/actionbar_background_light</item>
</style>
<style name="ActionBar.Dark" parent="@style/ActionBar">
<item name="android:background">@color/actionbar_background_dark</item>
</style>
<style name="AppTheme.Light" parent="@android:style/Theme.Holo.Light">
<item name="android:actionBarStyle">@style/ActionBar.Light</item>
<item name="android:windowActionBarOverlay">true</item>
<item name="listDragShadowBackground">@android:color/background_light</item>
<item name="menuIconCamera">@drawable/ic_menu_camera_holo_light</item>
<item name="menuIconToggle">@drawable/ic_menu_toggle_holo_light</item>
<item name="menuIconShare">@drawable/ic_menu_share_holo_light</item>
</style>
<style name="AppTheme.Dark" parent="@android:style/Theme.Holo">
<item name="android:actionBarStyle">@style/ActionBar.Dark</item>
<item name="android:windowActionBarOverlay">true</item>
<item name="listDragShadowBackground">@android:color/background_dark</item>
<item name="menuIconCamera">@drawable/ic_menu_camera_holo_dark</item>
<item name="menuIconToggle">@drawable/ic_menu_toggle_holo_dark</item>
<item name="menuIconShare">@drawable/ic_menu_share_holo_dark</item>
</style>
</resources>

View File

@@ -16,29 +16,45 @@
package com.example.android.hcgallery;
import java.util.StringTokenizer;
import android.app.ActionBar;
import android.app.Activity;
import android.app.Fragment;
import android.content.ClipData;
import android.content.ClipDescription;
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.ViewGroup;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.view.Window;
import android.view.WindowManager;
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);
@@ -47,47 +63,86 @@ public class ContentFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.content_welcome, null);
final ImageView imageView = (ImageView) view.findViewById(R.id.image);
view.setDrawingCacheEnabled(false);
mContentView = inflater.inflate(R.layout.content_welcome, null);
final ImageView imageView = (ImageView) mContentView.findViewById(R.id.image);
mContentView.setDrawingCacheEnabled(false);
view.setOnDragListener(new View.OnDragListener() {
mContentView.setOnDragListener(new View.OnDragListener() {
public boolean onDrag(View v, DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_ENTERED:
view.setBackgroundColor(Color.LTGRAY);
break;
case DragEvent.ACTION_DRAG_EXITED:
view.setBackgroundColor(Color.TRANSPARENT);
break;
case DragEvent.ACTION_DRAG_STARTED:
return processDragStarted(event);
case DragEvent.ACTION_DROP:
view.setBackgroundColor(Color.TRANSPARENT);
return processDrop(event, imageView);
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;
}
});
view.setOnClickListener(new OnClickListener() {
// 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.
public void onClick(View v) {
ActionBar bar = getActivity().getActionBar();
if (bar != null) {
if (bar.isShowing()) {
bar.hide();
} else {
bar.show();
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);
}
}
});
return view;
// 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) {
boolean processDragStarted(DragEvent event) {
// Determine whether to continue processing drag and drop based on the
// plain text mime type.
ClipDescription clipDesc = event.getClipDescription();
@@ -97,7 +152,7 @@ public class ContentFragment extends Fragment {
return false;
}
private boolean processDrop(DragEvent event, ImageView imageView) {
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();
@@ -130,16 +185,115 @@ public class ContentFragment extends Fragment {
return false;
}
public void updateContentAndRecycleBitmap(int category, int position) {
// Get the bitmap that needs to be drawn and update the ImageView
Bitmap next = Directory.getCategory(category).getEntry(position)
.getBitmap(getResources());
((ImageView) getView().findViewById(R.id.image)).setImageBitmap(next);
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;
}
};
}

View File

@@ -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);
}
}
}
}

View File

@@ -28,10 +28,15 @@ 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.graphics.drawable.ColorDrawable;
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;
@@ -39,9 +44,13 @@ 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"};
@@ -51,26 +60,18 @@ public class MainActivity extends Activity implements ActionBar.TabListener {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState != null && savedInstanceState.getInt("theme", -1) != -1) {
mThemeId = savedInstanceState.getInt("theme");
this.setTheme(mThemeId);
}
requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
setContentView(R.layout.main);
Directory.initializeDirectory();
ActionBar bar = getActionBar();
if (mThemeId == android.R.style.Theme_Holo_Light || mThemeId == -1) {
bar.setBackgroundDrawable(
new ColorDrawable(getResources().getColor(R.color.actionbar_background_light)));
} else {
bar.setBackgroundDrawable(
new ColorDrawable(getResources().getColor(R.color.actionbar_background_dark)));
}
int i;
for (i = 0; i < Directory.getCategoryCount(); i++) {
bar.addTab(bar.newTab().setText(Directory.getCategory(i).getName())
@@ -122,21 +123,30 @@ public class MainActivity extends Activity implements ActionBar.TabListener {
intent.putExtra("theme", mThemeId);
startActivity(intent);
return true;
case R.id.toggleTitles:
toggleVisibleTitles();
return true;
case R.id.toggleTheme:
if (mThemeId == android.R.style.Theme_Holo) {
mThemeId = android.R.style.Theme_Holo_Light;
if (mThemeId == R.style.AppTheme_Dark) {
mThemeId = R.style.AppTheme_Light;
} else {
mThemeId = android.R.style.Theme_Holo;
mThemeId = R.style.AppTheme_Dark;
}
this.recreate();
return true;
case R.id.showDialog:
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:
@@ -225,21 +235,84 @@ public class MainActivity extends Activity implements ActionBar.TabListener {
mCurrentTitlesAnimator = objectAnimator;
invalidateOptionsMenu();
// Manually trigger onNewIntent to check for ACTION_DIALOG.
onNewIntent(getIntent());
}
void showDialog() {
@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("The Dialog Of Awesome");
DialogFragment newFragment = MyDialogFragment.newInstance(text);
// Create and show the dialog.
// 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]);
@@ -261,24 +334,19 @@ public class MainActivity extends Activity implements ActionBar.TabListener {
public static MyDialogFragment newInstance(String title) {
MyDialogFragment frag = new MyDialogFragment();
Bundle args = new Bundle();
args.putString("title", title);
args.putString("text", title);
frag.setArguments(args);
return frag;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
String title = getArguments().getString("title");
String text = getArguments().getString("text");
return new AlertDialog.Builder(getActivity())
.setTitle(title)
.setPositiveButton("OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
}
}
)
.setNegativeButton("Cancel",
.setTitle("A Dialog of Awesome")
.setMessage(text)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
}

View File

@@ -20,6 +20,8 @@ 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;
@@ -28,6 +30,7 @@ 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;
@@ -53,47 +56,40 @@ public class TitlesFragment extends ListFragment {
populateTitles(mCategory);
ListView lv = getListView();
lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
selectPosition(mCurPosition);
lv.setCacheColorHint(Color.WHITE);
lv.setCacheColorHint(Color.TRANSPARENT);
lv.setOnItemLongClickListener(new OnItemLongClickListener() {
public boolean onItemLongClick(AdapterView<?> av, View v, int pos,
long id) {
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, title), null, 0);
v.startDrag(data, new MyDragShadowBuilder(v), null, 0);
return true;
}
});
selectPosition(mCurPosition);
}
private static class MyDragShadowBuilder extends View.DragShadowBuilder {
private static Drawable mShadow;
private static String mLabel;
private static int mViewHeight;
public MyDragShadowBuilder(View v, String label) {
private class MyDragShadowBuilder extends View.DragShadowBuilder {
private Drawable mShadow;
public MyDragShadowBuilder(View v) {
super(v);
mShadow = new ColorDrawable(Color.BLUE);
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());
mLabel = label;
mViewHeight = v.getHeight();
a.recycle();
}
@Override
public void onDrawShadow(Canvas canvas) {
super.onDrawShadow(canvas);
mShadow.draw(canvas);
Paint paint = new TextPaint();
paint.setTextSize(20);
paint.setColor(Color.LTGRAY);
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setAntiAlias(true);
canvas.drawText(mLabel, 20, (float) (mViewHeight * .6), paint);
getView().draw(canvas);
}
}
@@ -116,13 +112,13 @@ public class TitlesFragment extends ListFragment {
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