rewrite HC Gallery app to support phones. In summary, this change:
- moves tablet-style layouts to layout-large* directories - adds ContentActivity to host the ContentFragment when on phones - adds an OnItemSelectedListener interface to TitlesFragment, which MainActivity implements in order to receive callbacks on click events to the list item and then pass the selected item to the ContentFragment in the manner appropriate for the current configuration... Specifically, when in two-pane mode, it updates the ContentFragment directly, and when in single-pane mode, it starts the ContentActivity with intent data about the selected item, which then updates the ContentFragment - Change CameraSample.java to CameraActivity.java for name conventions - Moves all menu strings into string resources - Fixes camera sample to properly handle front-facing camera on other devices (was broken on nexus s and on g-slate) - Fixes camera sample to handle resume state after the camera has changed (for example, when switched to a different camera, it would crash on resume) - Moves various code around between classes as appropriate for the fragment handling the action. For example, move the ActionBar.TabListener implementation to the TitlesFragment (was originally impemented by the MainActivity) - Adds logic to support devices without camera and properly declare the camera in manifest as such - Maintains the state of hidden titles list across restarts Change-Id: I27a39a68dee37325c0c3607aa0b56ab6c134d026
This commit is contained in:
@@ -15,10 +15,16 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.android.hcgallery" android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
package="com.example.android.hcgallery" android:versionCode="2"
|
||||
android:versionName="1.2">
|
||||
|
||||
<uses-sdk android:minSdkVersion="11" />
|
||||
<uses-sdk android:minSdkVersion="11" android:targetSdkVersion="13" />
|
||||
|
||||
<!-- Declares that the app supports devices w/o touch, such as a mouse-driven device
|
||||
or a device that provides only a d-pad for navigation -->
|
||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||
<!-- The app uses the camera, but checks for it at runtime and will work without it -->
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
@@ -27,8 +33,7 @@
|
||||
android:icon="@drawable/icon"
|
||||
android:logo="@drawable/logo"
|
||||
android:theme="@style/AppTheme.Light"
|
||||
android:hardwareAccelerated="true"
|
||||
android:debuggable="true">
|
||||
android:hardwareAccelerated="true">
|
||||
|
||||
<activity android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
@@ -43,8 +48,11 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Holds the ContentFragment for small/normal screens -->
|
||||
<activity android:name=".ContentActivity" />
|
||||
|
||||
<!-- Camera Sample -->
|
||||
<activity android:name=".CameraSample"
|
||||
<activity android:name=".CameraActivity"
|
||||
android:label="@string/camera_sample"
|
||||
android:screenOrientation="landscape" />
|
||||
</application>
|
||||
|
||||
@@ -21,14 +21,12 @@
|
||||
android:id="@+id/frags">
|
||||
|
||||
<fragment class="com.example.android.hcgallery.TitlesFragment"
|
||||
android:id="@+id/frag_title"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="?android:attr/actionBarSize"
|
||||
android:id="@+id/titles_frag"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/titles_size" />
|
||||
|
||||
<fragment class="com.example.android.hcgallery.ContentFragment"
|
||||
android:id="@+id/frag_content"
|
||||
android:id="@+id/content_frag"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
33
samples/HoneycombGallery/res/layout-large/main.xml
Normal file
33
samples/HoneycombGallery/res/layout-large/main.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?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:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/frags">
|
||||
|
||||
<fragment class="com.example.android.hcgallery.TitlesFragment"
|
||||
android:id="@+id/titles_frag"
|
||||
android:layout_width="@dimen/titles_size"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<fragment class="com.example.android.hcgallery.ContentFragment"
|
||||
android:id="@+id/content_frag"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -15,10 +15,7 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<fragment android:name="com.example.android.hcgallery.CameraFragment"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2011 The Android Open Source Project
|
||||
|
||||
@@ -14,11 +15,11 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<TextView
|
||||
<fragment class="com.example.android.hcgallery.ContentFragment"
|
||||
android:id="@+id/content_frag"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">Hello Action Bar</TextView>
|
||||
</LinearLayout>
|
||||
android:layout_height="match_parent" />
|
||||
</FrameLayout>
|
||||
@@ -14,22 +14,13 @@
|
||||
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:orientation="horizontal"
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/frags">
|
||||
|
||||
<fragment class="com.example.android.hcgallery.TitlesFragment"
|
||||
android:id="@+id/frag_title"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="?android:attr/actionBarSize"
|
||||
android:layout_width="@dimen/titles_size"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<fragment class="com.example.android.hcgallery.ContentFragment"
|
||||
android:id="@+id/frag_content"
|
||||
android:layout_height="match_parent">
|
||||
<fragment
|
||||
class="com.example.android.hcgallery.TitlesFragment"
|
||||
android:id="@+id/titles_frag"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:paddingLeft="16dip"
|
||||
android:layout_width="0dip"
|
||||
android:paddingLeft="16dp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_vertical"
|
||||
@@ -54,7 +54,7 @@
|
||||
android:src="@drawable/btn_notification_ic_example"
|
||||
android:background="@null"
|
||||
android:layout_weight="0"
|
||||
android:layout_width="48dip"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -16,5 +16,7 @@
|
||||
-->
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/switch_cam" android:title="Switch Camera" />
|
||||
<item android:id="@+id/menu_switch_cam"
|
||||
android:title="@string/switch_camera"
|
||||
android:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
|
||||
@@ -16,25 +16,20 @@
|
||||
-->
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/camera"
|
||||
android:title="Camera"
|
||||
<item android:id="@+id/menu_camera"
|
||||
android:title="@string/camera"
|
||||
android:icon="?attr/menuIconCamera"
|
||||
android:showAsAction="ifRoom" />
|
||||
<item android:id="@+id/toggleTitles"
|
||||
<item android:id="@+id/menu_toggleTitles"
|
||||
android:title="@string/toggle_titles"
|
||||
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="never" />
|
||||
<item android:id="@+id/showDialog"
|
||||
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" />
|
||||
<item android:id="@+id/menu_toggleTheme"
|
||||
android:title="@string/day_night" />
|
||||
<item android:id="@+id/menu_showDialog"
|
||||
android:title="@string/show_dialog" />
|
||||
<item android:id="@+id/menu_showStandardNotification"
|
||||
android:title="@string/basic_notification" />
|
||||
<item android:id="@+id/menu_showCustomNotification"
|
||||
android:title="@string/custom_notification" />
|
||||
</menu>
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
-->
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/share"
|
||||
android:title="Share"
|
||||
<item android:id="@+id/menu_share"
|
||||
android:title="@string/share"
|
||||
android:icon="?attr/menuIconShare"
|
||||
android:showAsAction="always|withText" />
|
||||
</menu>
|
||||
|
||||
@@ -25,4 +25,14 @@
|
||||
<string name="notification_text">Example notification text</string>
|
||||
|
||||
<string name="photo_selection_cab_title">Photo selection</string>
|
||||
|
||||
<!-- Menu strings -->
|
||||
<string name="share">Share</string>
|
||||
<string name="camera">Camera</string>
|
||||
<string name="toggle_titles">Toggle Titles</string>
|
||||
<string name="day_night">Day/Night</string>
|
||||
<string name="show_dialog">Show a dialog</string>
|
||||
<string name="basic_notification">Show a basic notification</string>
|
||||
<string name="custom_notification">Show a custom notification</string>
|
||||
<string name="switch_camera">Switch camera</string>
|
||||
</resources>
|
||||
@@ -19,7 +19,8 @@ package com.example.android.hcgallery;
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class CameraSample extends Activity {
|
||||
/** Basic shell activity that hosts CameraFragment. */
|
||||
public class CameraActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -16,12 +16,9 @@
|
||||
|
||||
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.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.hardware.Camera;
|
||||
@@ -38,12 +35,16 @@ import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class CameraFragment extends Fragment {
|
||||
|
||||
private Preview mPreview;
|
||||
Camera mCamera;
|
||||
int mNumberOfCameras;
|
||||
int mCameraCurrentlyLocked;
|
||||
int mCurrentCamera; // Camera ID currently chosen
|
||||
int mCameraCurrentlyLocked; // Camera ID that's actually acquired
|
||||
|
||||
// The first rear facing camera
|
||||
int mDefaultCameraId;
|
||||
@@ -52,20 +53,18 @@ public class CameraFragment extends Fragment {
|
||||
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.
|
||||
// Create a container that will hold a SurfaceView for camera previews
|
||||
mPreview = new Preview(this.getActivity());
|
||||
|
||||
// Find the total number of cameras available
|
||||
mNumberOfCameras = Camera.getNumberOfCameras();
|
||||
|
||||
// Find the ID of the default camera
|
||||
// Find the ID of the rear-facing ("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;
|
||||
mCurrentCamera = mDefaultCameraId = i;
|
||||
}
|
||||
}
|
||||
setHasOptionsMenu(mNumberOfCameras > 1);
|
||||
@@ -95,9 +94,10 @@ public class CameraFragment extends Fragment {
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// Open the default i.e. the first rear facing camera.
|
||||
mCamera = Camera.open(mDefaultCameraId);
|
||||
mCameraCurrentlyLocked = mDefaultCameraId;
|
||||
// Use mCurrentCamera to select the camera desired to safely restore
|
||||
// the fragment after the camera has been changed
|
||||
mCamera = Camera.open(mCurrentCamera);
|
||||
mCameraCurrentlyLocked = mCurrentCamera;
|
||||
mPreview.setCamera(mCamera);
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ public class CameraFragment extends Fragment {
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
// Handle item selection
|
||||
switch (item.getItemId()) {
|
||||
case R.id.switch_cam:
|
||||
case R.id.menu_switch_cam:
|
||||
// Release this camera -> mCameraCurrentlyLocked
|
||||
if (mCamera != null) {
|
||||
mCamera.stopPreview();
|
||||
@@ -139,10 +139,9 @@ public class CameraFragment extends Fragment {
|
||||
|
||||
// Acquire the next camera and request Preview to reconfigure
|
||||
// parameters.
|
||||
mCamera = Camera
|
||||
.open((mCameraCurrentlyLocked + 1) % mNumberOfCameras);
|
||||
mCameraCurrentlyLocked = (mCameraCurrentlyLocked + 1)
|
||||
% mNumberOfCameras;
|
||||
mCurrentCamera = (mCameraCurrentlyLocked + 1) % mNumberOfCameras;
|
||||
mCamera = Camera.open(mCurrentCamera);
|
||||
mCameraCurrentlyLocked = mCurrentCamera;
|
||||
mPreview.switchCamera(mCamera);
|
||||
|
||||
// Start the preview
|
||||
@@ -152,6 +151,7 @@ public class CameraFragment extends Fragment {
|
||||
Intent intent = new Intent(this.getActivity(), MainActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
@@ -175,6 +175,7 @@ class Preview extends ViewGroup implements SurfaceHolder.Callback {
|
||||
Size mPreviewSize;
|
||||
List<Size> mSupportedPreviewSizes;
|
||||
Camera mCamera;
|
||||
boolean mSurfaceCreated = false;
|
||||
|
||||
Preview(Context context) {
|
||||
super(context);
|
||||
@@ -194,7 +195,7 @@ class Preview extends ViewGroup implements SurfaceHolder.Callback {
|
||||
if (mCamera != null) {
|
||||
mSupportedPreviewSizes = mCamera.getParameters()
|
||||
.getSupportedPreviewSizes();
|
||||
requestLayout();
|
||||
if (mSurfaceCreated) requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,11 +206,6 @@ class Preview extends ViewGroup implements SurfaceHolder.Callback {
|
||||
} 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
|
||||
@@ -227,11 +223,18 @@ class Preview extends ViewGroup implements SurfaceHolder.Callback {
|
||||
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width,
|
||||
height);
|
||||
}
|
||||
|
||||
if (mCamera != null) {
|
||||
Camera.Parameters parameters = mCamera.getParameters();
|
||||
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
|
||||
|
||||
mCamera.setParameters(parameters);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
if (changed && getChildCount() > 0) {
|
||||
if (getChildCount() > 0) {
|
||||
final View child = getChildAt(0);
|
||||
|
||||
final int width = r - l;
|
||||
@@ -269,6 +272,8 @@ class Preview extends ViewGroup implements SurfaceHolder.Callback {
|
||||
} catch (IOException exception) {
|
||||
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
|
||||
}
|
||||
if (mPreviewSize == null) requestLayout();
|
||||
mSurfaceCreated = true;
|
||||
}
|
||||
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
|
||||
package com.example.android.hcgallery;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
/** This is a shell activity that hosts ContentFragment when the device screen
|
||||
* is smaller than "large".
|
||||
*/
|
||||
public class ContentActivity extends Activity {
|
||||
private int mThemeId = 0;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Bundle extras = getIntent().getExtras();
|
||||
if (extras != null) {
|
||||
// The activity theme is the only state data that the activity needs
|
||||
// to restore. All info about the content displayed is managed by the fragment
|
||||
mThemeId = extras.getInt("theme");
|
||||
} else if (savedInstanceState != null) {
|
||||
// If there's no restore state, get the theme from the intent
|
||||
mThemeId = savedInstanceState.getInt("theme");
|
||||
}
|
||||
|
||||
if (mThemeId != 0) {
|
||||
setTheme(mThemeId);
|
||||
}
|
||||
|
||||
setContentView(R.layout.content_activity);
|
||||
|
||||
if (extras != null) {
|
||||
// Take the info from the intent and deliver it to the fragment so it can update
|
||||
int category = extras.getInt("category");
|
||||
int position = extras.getInt("position");
|
||||
ContentFragment frag = (ContentFragment) getFragmentManager().findFragmentById(R.id.content_frag);
|
||||
frag.updateContentAndRecycleBitmap(category, position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt("theme", mThemeId);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
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;
|
||||
@@ -37,6 +36,8 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
@@ -46,8 +47,16 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/** Fragment that shows the content selected from the TitlesFragment.
|
||||
* When running on a screen size smaller than "large", this fragment is hosted in
|
||||
* ContentActivity. Otherwise, it appears side by side with the TitlesFragment
|
||||
* in MainActivity. */
|
||||
public class ContentFragment extends Fragment {
|
||||
private View mContentView;
|
||||
private int mCategory = 0;
|
||||
private int mCurPosition = 0;
|
||||
private boolean mSystemUiVisible = true;
|
||||
private boolean mSoloFragment = false;
|
||||
|
||||
// The bitmap currently used by ImageView
|
||||
private Bitmap mBitmap = null;
|
||||
@@ -55,11 +64,9 @@ public class ContentFragment extends Fragment {
|
||||
// Current action mode (contextual action bar, a.k.a. CAB)
|
||||
private ActionMode mCurrentActionMode;
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
}
|
||||
|
||||
/** This is where we initialize the fragment's UI and attach some
|
||||
* event listeners to UI components.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -67,65 +74,48 @@ public class ContentFragment extends Fragment {
|
||||
final ImageView imageView = (ImageView) mContentView.findViewById(R.id.image);
|
||||
mContentView.setDrawingCacheEnabled(false);
|
||||
|
||||
// Handle drag events when a list item is dragged into the view
|
||||
mContentView.setOnDragListener(new View.OnDragListener() {
|
||||
public boolean onDrag(View v, DragEvent event) {
|
||||
public boolean onDrag(View view, DragEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case DragEvent.ACTION_DRAG_ENTERED:
|
||||
mContentView.setBackgroundColor(
|
||||
view.setBackgroundColor(
|
||||
getResources().getColor(R.color.drag_active_color));
|
||||
break;
|
||||
|
||||
case DragEvent.ACTION_DRAG_EXITED:
|
||||
mContentView.setBackgroundColor(Color.TRANSPARENT);
|
||||
view.setBackgroundColor(Color.TRANSPARENT);
|
||||
break;
|
||||
|
||||
case DragEvent.ACTION_DRAG_STARTED:
|
||||
return processDragStarted(event);
|
||||
|
||||
case DragEvent.ACTION_DROP:
|
||||
mContentView.setBackgroundColor(Color.TRANSPARENT);
|
||||
view.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.
|
||||
|
||||
// Show/hide the system status bar when single-clicking a photo.
|
||||
mContentView.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
if (mContentView.getSystemUiVisibility() == View.STATUS_BAR_VISIBLE) {
|
||||
mContentView.setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
|
||||
public void onClick(View view) {
|
||||
if (mCurrentActionMode != null) {
|
||||
// If we're in an action mode, don't toggle the action bar
|
||||
return;
|
||||
}
|
||||
|
||||
if (mSystemUiVisible) {
|
||||
setSystemUiVisible(false);
|
||||
} else {
|
||||
mContentView.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
|
||||
setSystemUiVisible(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 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) {
|
||||
@@ -134,7 +124,7 @@ public class ContentFragment extends Fragment {
|
||||
|
||||
mCurrentActionMode = getActivity().startActionMode(
|
||||
mContentSelectionActionModeCallback);
|
||||
mContentView.setSelected(true);
|
||||
view.setSelected(true);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -142,6 +132,99 @@ public class ContentFragment extends Fragment {
|
||||
return mContentView;
|
||||
}
|
||||
|
||||
/** This is where we perform additional setup for the fragment that's either
|
||||
* not related to the fragment's layout or must be done after the layout is drawn.
|
||||
*/
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
// Set member variable for whether this fragment is the only one in the activity
|
||||
Fragment listFragment = getFragmentManager().findFragmentById(R.id.titles_frag);
|
||||
mSoloFragment = listFragment == null ? true : false;
|
||||
|
||||
if (mSoloFragment) {
|
||||
// The fragment is alone, so enable up navigation
|
||||
getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
// Must call in order to get callback to onOptionsItemSelected()
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
// Current position and UI visibility should survive screen rotations.
|
||||
if (savedInstanceState != null) {
|
||||
setSystemUiVisible(savedInstanceState.getBoolean("systemUiVisible"));
|
||||
if (mSoloFragment) {
|
||||
// Restoring these members is not necessary when this fragment
|
||||
// is combined with the TitlesFragment, because when the TitlesFragment
|
||||
// is restored, it selects the appropriate item and sends the event
|
||||
// to the updateContentAndRecycleBitmap() method itself
|
||||
mCategory = savedInstanceState.getInt("category");
|
||||
mCurPosition = savedInstanceState.getInt("listPosition");
|
||||
updateContentAndRecycleBitmap(mCategory, mCurPosition);
|
||||
}
|
||||
}
|
||||
|
||||
if (mSoloFragment) {
|
||||
String title = Directory.getCategory(mCategory).getEntry(mCurPosition).getName();
|
||||
ActionBar bar = getActivity().getActionBar();
|
||||
bar.setTitle(title);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
// This callback is used only when mSoloFragment == true (see onActivityCreated above)
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
// App icon in Action Bar clicked; go up
|
||||
Intent intent = new Intent(getActivity(), MainActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // Reuse the existing instance
|
||||
startActivity(intent);
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState (Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt("listPosition", mCurPosition);
|
||||
outState.putInt("category", mCategory);
|
||||
outState.putBoolean("systemUiVisible", mSystemUiVisible);
|
||||
}
|
||||
|
||||
/** Toggle whether the system UI (status bar / system bar) is visible.
|
||||
* This also toggles the action bar visibility.
|
||||
* @param show True to show the system UI, false to hide it.
|
||||
*/
|
||||
void setSystemUiVisible(boolean show) {
|
||||
mSystemUiVisible = show;
|
||||
|
||||
Window window = getActivity().getWindow();
|
||||
WindowManager.LayoutParams winParams = window.getAttributes();
|
||||
View view = getView();
|
||||
ActionBar actionBar = getActivity().getActionBar();
|
||||
|
||||
if (show) {
|
||||
// Show status bar (remove fullscreen flag)
|
||||
window.setFlags(0, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
// Show system bar
|
||||
view.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
|
||||
// Show action bar
|
||||
actionBar.show();
|
||||
} else {
|
||||
// Add fullscreen flag (hide status bar)
|
||||
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
// Hide system bar
|
||||
view.setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
|
||||
// Hide action bar
|
||||
actionBar.hide();
|
||||
}
|
||||
window.setAttributes(winParams);
|
||||
}
|
||||
|
||||
boolean processDragStarted(DragEvent event) {
|
||||
// Determine whether to continue processing drag and drop based on the
|
||||
// plain text mime type.
|
||||
@@ -176,7 +259,7 @@ public class ContentFragment extends Fragment {
|
||||
updateContentAndRecycleBitmap(category, entryId);
|
||||
// Update list fragment with selected entry.
|
||||
TitlesFragment titlesFrag = (TitlesFragment)
|
||||
getFragmentManager().findFragmentById(R.id.frag_title);
|
||||
getFragmentManager().findFragmentById(R.id.titles_frag);
|
||||
titlesFrag.selectPosition(entryId);
|
||||
return true;
|
||||
}
|
||||
@@ -185,7 +268,15 @@ public class ContentFragment extends Fragment {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current image visible.
|
||||
* @param category Index position of the image category
|
||||
* @param position Index position of the image
|
||||
*/
|
||||
void updateContentAndRecycleBitmap(int category, int position) {
|
||||
mCategory = category;
|
||||
mCurPosition = position;
|
||||
|
||||
if (mCurrentActionMode != null) {
|
||||
mCurrentActionMode.finish();
|
||||
}
|
||||
@@ -203,6 +294,9 @@ public class ContentFragment extends Fragment {
|
||||
((ImageView) getView().findViewById(R.id.image)).setImageBitmap(mBitmap);
|
||||
}
|
||||
|
||||
/** Share the currently selected photo using an AsyncTask to compress the image
|
||||
* and then invoke the appropriate share intent.
|
||||
*/
|
||||
void shareCurrentPhoto() {
|
||||
File externalCacheDir = getActivity().getExternalCacheDir();
|
||||
if (externalCacheDir == null) {
|
||||
@@ -229,6 +323,7 @@ public class ContentFragment extends Fragment {
|
||||
* Compress and write the bitmap to disk on a separate thread.
|
||||
* @return TRUE if the write was successful, FALSE otherwise.
|
||||
*/
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... voids) {
|
||||
try {
|
||||
FileOutputStream fo = new FileOutputStream(tempFile, false);
|
||||
@@ -250,6 +345,7 @@ public class ContentFragment extends Fragment {
|
||||
* 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.
|
||||
*/
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
if (result != Boolean.TRUE) {
|
||||
return;
|
||||
@@ -283,7 +379,7 @@ public class ContentFragment extends Fragment {
|
||||
|
||||
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
|
||||
switch (menuItem.getItemId()) {
|
||||
case R.id.share:
|
||||
case R.id.menu_share:
|
||||
shareCurrentPhoto();
|
||||
actionMode.finish();
|
||||
return true;
|
||||
|
||||
@@ -33,6 +33,7 @@ import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
@@ -43,92 +44,86 @@ 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 {
|
||||
/** This is the main "launcher" activity.
|
||||
* When running on a "large" or larger screen, this activity displays both the
|
||||
* TitlesFragments and the Content Fragment. When on a smaller screen size, this
|
||||
* activity displays only the TitlesFragment. In which case, selecting a list
|
||||
* item opens the ContentActivity, holds only the ContentFragment. */
|
||||
public class MainActivity extends Activity implements TitlesFragment.OnItemSelectedListener {
|
||||
|
||||
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 static final int NOTIFICATION_DEFAULT = 1;
|
||||
private static final String ACTION_DIALOG = "com.example.android.hcgallery.action.DIALOG";
|
||||
private int mThemeId = -1;
|
||||
private boolean mDualFragments = false;
|
||||
private boolean mTitlesHidden = false;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if(savedInstanceState != null && savedInstanceState.getInt("theme", -1) != -1) {
|
||||
if(savedInstanceState != null) {
|
||||
if (savedInstanceState.getInt("theme", -1) != -1) {
|
||||
mThemeId = savedInstanceState.getInt("theme");
|
||||
this.setTheme(mThemeId);
|
||||
}
|
||||
mTitlesHidden = savedInstanceState.getBoolean("titlesHidden");
|
||||
}
|
||||
|
||||
setContentView(R.layout.main);
|
||||
|
||||
Directory.initializeDirectory();
|
||||
|
||||
ActionBar bar = getActionBar();
|
||||
bar.setDisplayShowTitleEnabled(false);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < Directory.getCategoryCount(); i++) {
|
||||
bar.addTab(bar.newTab().setText(Directory.getCategory(i).getName())
|
||||
.setTabListener(this));
|
||||
ContentFragment frag = (ContentFragment) getFragmentManager()
|
||||
.findFragmentById(R.id.content_frag);
|
||||
if (frag != null) mDualFragments = true;
|
||||
|
||||
if (mTitlesHidden) {
|
||||
getFragmentManager().beginTransaction()
|
||||
.hide(getFragmentManager().findFragmentById(R.id.titles_frag)).commit();
|
||||
}
|
||||
|
||||
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);
|
||||
// If the device doesn't support camera, remove the camera menu item
|
||||
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
|
||||
menu.removeItem(R.id.menu_camera);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
// If not showing both fragments, remove the "toggle titles" menu item
|
||||
if (!mDualFragments) {
|
||||
menu.removeItem(R.id.menu_toggleTitles);
|
||||
} else {
|
||||
menu.findItem(R.id.menu_toggleTitles).setTitle(mToggleLabels[mTitlesHidden ? 0 : 1]);
|
||||
}
|
||||
return super.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.camera:
|
||||
Intent intent = new Intent(this, CameraSample.class);
|
||||
case R.id.menu_camera:
|
||||
Intent intent = new Intent(this, CameraActivity.class);
|
||||
intent.putExtra("theme", mThemeId);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
|
||||
case R.id.toggleTitles:
|
||||
case R.id.menu_toggleTitles:
|
||||
toggleVisibleTitles();
|
||||
return true;
|
||||
|
||||
case R.id.toggleTheme:
|
||||
case R.id.menu_toggleTheme:
|
||||
if (mThemeId == R.style.AppTheme_Dark) {
|
||||
mThemeId = R.style.AppTheme_Light;
|
||||
} else {
|
||||
@@ -137,15 +132,15 @@ public class MainActivity extends Activity implements ActionBar.TabListener {
|
||||
this.recreate();
|
||||
return true;
|
||||
|
||||
case R.id.showDialog:
|
||||
case R.id.menu_showDialog:
|
||||
showDialog("This is indeed an awesome dialog.");
|
||||
return true;
|
||||
|
||||
case R.id.showStandardNotification:
|
||||
case R.id.menu_showStandardNotification:
|
||||
showNotification(false);
|
||||
return true;
|
||||
|
||||
case R.id.showCustomNotification:
|
||||
case R.id.menu_showCustomNotification:
|
||||
showNotification(true);
|
||||
return true;
|
||||
|
||||
@@ -154,13 +149,13 @@ public class MainActivity extends Activity implements ActionBar.TabListener {
|
||||
}
|
||||
}
|
||||
|
||||
/** Respond to the "toogle titles" item in the action bar */
|
||||
public void toggleVisibleTitles() {
|
||||
// Use these for custom animations.
|
||||
final FragmentManager fm = getFragmentManager();
|
||||
final TitlesFragment f = (TitlesFragment) fm
|
||||
.findFragmentById(R.id.frag_title);
|
||||
.findFragmentById(R.id.titles_frag);
|
||||
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.
|
||||
@@ -207,6 +202,8 @@ public class MainActivity extends Activity implements ActionBar.TabListener {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
mCurrentTitlesAnimator = null;
|
||||
mTitlesHidden = false;
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -226,6 +223,8 @@ public class MainActivity extends Activity implements ActionBar.TabListener {
|
||||
return;
|
||||
mCurrentTitlesAnimator = null;
|
||||
fm.beginTransaction().hide(f).commit();
|
||||
mTitlesHidden = true;
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -234,8 +233,6 @@ public class MainActivity extends Activity implements ActionBar.TabListener {
|
||||
objectAnimator.start();
|
||||
mCurrentTitlesAnimator = objectAnimator;
|
||||
|
||||
invalidateOptionsMenu();
|
||||
|
||||
// Manually trigger onNewIntent to check for ACTION_DIALOG.
|
||||
onNewIntent(getIntent());
|
||||
}
|
||||
@@ -313,22 +310,36 @@ public class MainActivity extends Activity implements ActionBar.TabListener {
|
||||
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);
|
||||
outState.putBoolean("titlesHidden", mTitlesHidden);
|
||||
}
|
||||
|
||||
/** Implementation for TitlesFragment.OnItemSelectedListener.
|
||||
* When the TitlesFragment receives an onclick event for a list item,
|
||||
* it's passed back to this activity through this method so that we can
|
||||
* deliver it to the ContentFragment in the manner appropriate */
|
||||
public void onItemSelected(int category, int position) {
|
||||
if (!mDualFragments) {
|
||||
// If showing only the TitlesFragment, start the ContentActivity and
|
||||
// pass it the info about the selected item
|
||||
Intent intent = new Intent(this, ContentActivity.class);
|
||||
intent.putExtra("category", category);
|
||||
intent.putExtra("position", position);
|
||||
intent.putExtra("theme", mThemeId);
|
||||
startActivity(intent);
|
||||
} else {
|
||||
// If showing both fragments, directly update the ContentFragment
|
||||
ContentFragment frag = (ContentFragment) getFragmentManager()
|
||||
.findFragmentById(R.id.content_frag);
|
||||
frag.updateContentAndRecycleBitmap(category, position);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Dialog implementation that shows a simple dialog as a fragment */
|
||||
public static class MyDialogFragment extends DialogFragment {
|
||||
|
||||
public static MyDialogFragment newInstance(String title) {
|
||||
|
||||
@@ -16,47 +16,101 @@
|
||||
|
||||
package com.example.android.hcgallery;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.app.FragmentTransaction;
|
||||
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.view.ViewTreeObserver;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemLongClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.FrameLayout.LayoutParams;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.AdapterView.OnItemLongClickListener;
|
||||
|
||||
public class TitlesFragment extends ListFragment {
|
||||
/**
|
||||
* Fragment that shows the list of images
|
||||
* As an extension of ListFragment, this fragment uses a default layout
|
||||
* that includes a single ListView, which you can acquire with getListView()
|
||||
* When running on a screen size smaller than "large", this fragment appears alone
|
||||
* in MainActivity. In this case, selecting a list item opens the ContentActivity,
|
||||
* which likewise holds only the ContentFragment.
|
||||
*/
|
||||
public class TitlesFragment extends ListFragment implements ActionBar.TabListener {
|
||||
OnItemSelectedListener mListener;
|
||||
private int mCategory = 0;
|
||||
private int mCurPosition = 0;
|
||||
private boolean mDualFragments = false;
|
||||
|
||||
/** Container Activity must implement this interface and we ensure
|
||||
* that it does during the onAttach() callback
|
||||
*/
|
||||
public interface OnItemSelectedListener {
|
||||
public void onItemSelected(int category, int position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
// Check that the container activity has implemented the callback interface
|
||||
try {
|
||||
mListener = (OnItemSelectedListener) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(activity.toString()
|
||||
+ " must implement OnItemSelectedListener");
|
||||
}
|
||||
}
|
||||
|
||||
/** This is where we perform setup for the fragment that's either
|
||||
* not related to the fragment's layout or must be done after the layout is drawn.
|
||||
* Notice that this fragment does not implement onCreateView(), because it extends
|
||||
* ListFragment, which includes a ListView as the root view by default, so there's
|
||||
* no need to set up the layout.
|
||||
*/
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
ContentFragment frag = (ContentFragment) getFragmentManager()
|
||||
.findFragmentById(R.id.content_frag);
|
||||
if (frag != null) mDualFragments = true;
|
||||
|
||||
ActionBar bar = getActivity().getActionBar();
|
||||
bar.setDisplayHomeAsUpEnabled(false);
|
||||
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
|
||||
|
||||
// Must call in order to get callback to onCreateOptionsMenu()
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
Directory.initializeDirectory();
|
||||
for (int i = 0; i < Directory.getCategoryCount(); i++) {
|
||||
bar.addTab(bar.newTab().setText(Directory.getCategory(i).getName())
|
||||
.setTabListener(this));
|
||||
}
|
||||
|
||||
//Current position should survive screen rotations.
|
||||
if (savedInstanceState != null) {
|
||||
mCategory = savedInstanceState.getInt("category");
|
||||
mCurPosition = savedInstanceState.getInt("listPosition");
|
||||
bar.selectTab(bar.getTabAt(mCategory));
|
||||
}
|
||||
|
||||
populateTitles(mCategory);
|
||||
ListView lv = getListView();
|
||||
lv.setCacheColorHint(Color.TRANSPARENT); // Improves scrolling performance
|
||||
|
||||
if (mDualFragments) {
|
||||
// Highlight the currently selected item
|
||||
lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
||||
lv.setCacheColorHint(Color.TRANSPARENT);
|
||||
// Enable drag and dropping
|
||||
lv.setOnItemLongClickListener(new OnItemLongClickListener() {
|
||||
public boolean onItemLongClick(AdapterView<?> av, View v, int pos, long id) {
|
||||
final String title = (String) ((TextView) v).getText();
|
||||
@@ -68,10 +122,66 @@ public class TitlesFragment extends ListFragment {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
selectPosition(mCurPosition);
|
||||
}
|
||||
|
||||
// If showing both fragments, select the appropriate list item by default
|
||||
if (mDualFragments) selectPosition(mCurPosition);
|
||||
|
||||
// Attach a GlobalLayoutListener so that we get a callback when the layout
|
||||
// has finished drawing. This is necessary so that we can apply top-margin
|
||||
// to the ListView in order to dodge the ActionBar. Ordinarily, that's not
|
||||
// necessary, but we've set the ActionBar to "overlay" mode using our theme,
|
||||
// so the layout does not account for the action bar position on its own.
|
||||
ViewTreeObserver observer = getListView().getViewTreeObserver();
|
||||
observer.addOnGlobalLayoutListener(layoutListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
// Always detach ViewTreeObserver listeners when the view tears down
|
||||
getListView().getViewTreeObserver().removeGlobalOnLayoutListener(layoutListener);
|
||||
}
|
||||
|
||||
/** Attaches an adapter to the fragment's ListView to populate it with items */
|
||||
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();
|
||||
// Convenience method to attach an adapter to ListFragment's ListView
|
||||
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) {
|
||||
// Send the event to the host activity via OnItemSelectedListener callback
|
||||
mListener.onItemSelected(mCategory, position);
|
||||
mCurPosition = position;
|
||||
}
|
||||
|
||||
/** Called to select an item from the listview */
|
||||
public void selectPosition(int position) {
|
||||
// Only if we're showing both fragments should the item be "highlighted"
|
||||
if (mDualFragments) {
|
||||
ListView lv = getListView();
|
||||
lv.setItemChecked(position, true);
|
||||
}
|
||||
// Calls the parent activity's implementation of the OnItemSelectedListener
|
||||
// so the activity can pass the event to the sibling fragment as appropriate
|
||||
mListener.onItemSelected(mCategory, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState (Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt("listPosition", mCurPosition);
|
||||
outState.putInt("category", mCategory);
|
||||
}
|
||||
|
||||
/** This defines how the draggable list items appear during a drag event */
|
||||
private class MyDragShadowBuilder extends View.DragShadowBuilder {
|
||||
private Drawable mShadow;
|
||||
|
||||
@@ -93,38 +203,53 @@ public class TitlesFragment extends ListFragment {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Because the fragment doesn't have a reliable callback to notify us when
|
||||
// the activity's layout is completely drawn, this OnGlobalLayoutListener provides
|
||||
// the necessary callback so we can add top-margin to the ListView in order to dodge
|
||||
// the ActionBar. Which is necessary because the ActionBar is in overlay mode, meaning
|
||||
// that it will ordinarily sit on top of the activity layout as a top layer and
|
||||
// the ActionBar height can vary. Specifically, when on a small/normal size screen,
|
||||
// the action bar tabs appear in a second row, making the action bar twice as tall.
|
||||
ViewTreeObserver.OnGlobalLayoutListener layoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
||||
updateImage(position);
|
||||
public void onGlobalLayout() {
|
||||
int barHeight = getActivity().getActionBar().getHeight();
|
||||
ListView listView = getListView();
|
||||
FrameLayout.LayoutParams params = (LayoutParams) listView.getLayoutParams();
|
||||
// The list view top-margin should always match the action bar height
|
||||
if (params.topMargin != barHeight) {
|
||||
params.topMargin = barHeight;
|
||||
listView.setLayoutParams(params);
|
||||
}
|
||||
// The action bar doesn't update its height when hidden, so make top-margin zero
|
||||
if (!getActivity().getActionBar().isShowing()) {
|
||||
params.topMargin = 0;
|
||||
listView.setLayoutParams(params);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* The following are callbacks implemented for the ActionBar.TabListener,
|
||||
* which this fragment implements to handle events when tabs are selected.
|
||||
*/
|
||||
|
||||
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
|
||||
TitlesFragment titleFrag = (TitlesFragment) getFragmentManager()
|
||||
.findFragmentById(R.id.titles_frag);
|
||||
titleFrag.populateTitles(tab.getPosition());
|
||||
|
||||
if (mDualFragments) {
|
||||
titleFrag.selectPosition(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateImage(int position) {
|
||||
ContentFragment frag = (ContentFragment) getFragmentManager()
|
||||
.findFragmentById(R.id.frag_content);
|
||||
frag.updateContentAndRecycleBitmap(mCategory, position);
|
||||
mCurPosition = position;
|
||||
/* These must be implemented, but we don't use them */
|
||||
|
||||
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
|
||||
}
|
||||
|
||||
public void selectPosition(int position) {
|
||||
ListView lv = getListView();
|
||||
lv.setItemChecked(position, true);
|
||||
updateImage(position);
|
||||
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState (Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt("listPosition", mCurPosition);
|
||||
outState.putInt("category", mCategory);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user