diff --git a/build/sdk.atree b/build/sdk.atree
index 5025ba5ea..f4d100461 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -83,25 +83,27 @@ external/clang/lib/Headers build-tools/${PLATFORM_NAME}/rende
external/clang/LICENSE.TXT build-tools/${PLATFORM_NAME}/renderscript/clang-include/LICENSE.TXT
prebuilts/sdk/renderscript/lib/javalib.jar build-tools/${PLATFORM_NAME}/renderscript/lib/renderscript-v8.jar
-prebuilts/sdk/renderscript/lib/arm/libclcore.bc build-tools/${PLATFORM_NAME}/renderscript/lib/libclcore.bc
prebuilts/sdk/renderscript/lib/arm/libc.so build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/armeabi-v7a/libc.so
prebuilts/sdk/renderscript/lib/arm/libm.so build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/armeabi-v7a/libm.so
prebuilts/sdk/renderscript/lib/arm/libcompiler_rt.a build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/armeabi-v7a/libcompiler_rt.a
prebuilts/sdk/renderscript/lib/arm/libRSSupport.so build-tools/${PLATFORM_NAME}/renderscript/lib/packaged/armeabi-v7a/libRSSupport.so
prebuilts/sdk/renderscript/lib/arm/librsjni.so build-tools/${PLATFORM_NAME}/renderscript/lib/packaged/armeabi-v7a/librsjni.so
+prebuilts/sdk/renderscript/lib/arm/libclcore.bc build-tools/${PLATFORM_NAME}/renderscript/lib/bc/armeabi-v7a/libclcore.bc
prebuilts/sdk/renderscript/lib/mips/libc.so build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/mips/libc.so
prebuilts/sdk/renderscript/lib/mips/libm.so build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/mips/libm.so
prebuilts/sdk/renderscript/lib/mips/libcompiler_rt.a build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/mips/libcompiler_rt.a
prebuilts/sdk/renderscript/lib/mips/libRSSupport.so build-tools/${PLATFORM_NAME}/renderscript/lib/packaged/mips/libRSSupport.so
prebuilts/sdk/renderscript/lib/mips/librsjni.so build-tools/${PLATFORM_NAME}/renderscript/lib/packaged/mips/librsjni.so
+prebuilts/sdk/renderscript/lib/mips/libclcore.bc build-tools/${PLATFORM_NAME}/renderscript/lib/bc/mips/libclcore.bc
prebuilts/sdk/renderscript/lib/x86/libc.so build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/x86/libc.so
prebuilts/sdk/renderscript/lib/x86/libm.so build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/x86/libm.so
prebuilts/sdk/renderscript/lib/x86/libcompiler_rt.a build-tools/${PLATFORM_NAME}/renderscript/lib/intermediates/x86/libcompiler_rt.a
prebuilts/sdk/renderscript/lib/x86/libRSSupport.so build-tools/${PLATFORM_NAME}/renderscript/lib/packaged/x86/libRSSupport.so
prebuilts/sdk/renderscript/lib/x86/librsjni.so build-tools/${PLATFORM_NAME}/renderscript/lib/packaged/x86/librsjni.so
+prebuilts/sdk/renderscript/lib/x86/libclcore.bc build-tools/${PLATFORM_NAME}/renderscript/lib/bc/x86/libclcore.bc
@@ -168,6 +170,7 @@ docs/features.txt platforms/${PLATFORM_NAME}/data/features.txt
# fonts for layoutlib.
frameworks/base/data/fonts platforms/${PLATFORM_NAME}/data/fonts
+external/noto-fonts platforms/${PLATFORM_NAME}/data/fonts
# NOTICE files are copied by build/core/Makefile from sdk.git
sdk/files/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/templates/NOTICE.txt
@@ -218,14 +221,51 @@ development/samples/samples_source.properties samples/${PLATFORM_NAME}/source.p
# Note: Some samples reference a shared "common" directory. In the future
# this will be copied in automatically via a templating system. For now,
# we need to copy it in here as needed.
-developers/samples/android/connectivity samples/${PLATFORM_NAME}/connectivity
-developers/samples/android/common samples/${PLATFORM_NAME}/connectivity/sync/BasicSyncAdapter/BasicSyncAdapter/src/main/java/com/example/android/common
-developers/samples/android/content samples/${PLATFORM_NAME}/content
-developers/samples/android/input samples/${PLATFORM_NAME}/input
-developers/samples/android/media samples/${PLATFORM_NAME}/media
-developers/samples/android/security samples/${PLATFORM_NAME}/security
-developers/samples/android/testing samples/${PLATFORM_NAME}/testing
-developers/samples/android/ui samples/${PLATFORM_NAME}/ui
+developers/build/prebuilts/gradle/BasicAndroidKeyStore/ samples/${PLATFORM_NAME}/security/BasicAndroidKeyStore
+developers/build/prebuilts/gradle/BasicSyncAdapter/ samples/${PLATFORM_NAME}/connectivity/BasicSyncAdapter
+developers/build/prebuilts/gradle/NetworkConnect/ samples/${PLATFORM_NAME}/connectivity/NetworkConnect
+developers/build/prebuilts/gradle/BasicNetworking/ samples/${PLATFORM_NAME}/connectivity/BasicNetworking
+developers/build/prebuilts/gradle/BluetoothLeGatt/ samples/${PLATFORM_NAME}/connectivity/BluetoothLeGatt
+developers/build/prebuilts/gradle/AppRestrictions/ samples/${PLATFORM_NAME}/content/AppRestrictions
+developers/build/prebuilts/gradle/BasicContactables/ samples/${PLATFORM_NAME}/content/BasicContactables
+developers/build/prebuilts/gradle/StorageClient/ samples/${PLATFORM_NAME}/content/StorageClient
+developers/build/prebuilts/gradle/StorageProvider/ samples/${PLATFORM_NAME}/content/StorageProvider
+developers/build/prebuilts/gradle/BasicGestureDetect/ samples/${PLATFORM_NAME}/input/BasicGestureDetect
+developers/build/prebuilts/gradle/BasicMultitouch/ samples/${PLATFORM_NAME}/input/BasicMultitouch
+developers/build/prebuilts/gradle/ActivityInstrumentation/ samples/${PLATFORM_NAME}/testing/ActivityInstrumentation
+developers/build/prebuilts/gradle/MediaRecorder/ samples/${PLATFORM_NAME}/media/MediaRecorder
+developers/build/prebuilts/gradle/BasicMediaRouter/ samples/${PLATFORM_NAME}/media/BasicMediaRouter
+developers/build/prebuilts/gradle/BasicMediaDecoder/ samples/${PLATFORM_NAME}/media/BasicMediaDecoder
+developers/build/prebuilts/gradle/BorderlessButtons/ samples/${PLATFORM_NAME}/ui/BorderlessButtons
+developers/build/prebuilts/gradle/BasicAccessibility/ samples/${PLATFORM_NAME}/ui/BasicAccessibility
+developers/build/prebuilts/gradle/CustomChoiceList/ samples/${PLATFORM_NAME}/ui/CustomChoiceList
+developers/build/prebuilts/gradle/TextSwitcher/ samples/${PLATFORM_NAME}/ui/TextSwitcher
+developers/build/prebuilts/gradle/HorizontalPaging/ samples/${PLATFORM_NAME}/ui/HorizontalPaging
+developers/build/prebuilts/gradle/ActionBarCompat-Styled/ samples/${PLATFORM_NAME}/ui/ActionBarCompat-Styled
+developers/build/prebuilts/gradle/ActionBarCompat-ListPopupMenu/ samples/${PLATFORM_NAME}/ui/ActionBarCompat-ListPopupMenu
+developers/build/prebuilts/gradle/ActionBarCompat-ShareActionProvider/ samples/${PLATFORM_NAME}/ui/ActionBarCompat-ShareActionProvider
+developers/build/prebuilts/gradle/ActionBarCompat-Basic/ samples/${PLATFORM_NAME}/ui/ActionBarCompat-Basic
+developers/build/prebuilts/gradle/BasicNotifications/ samples/${PLATFORM_NAME}/ui/BasicNotifications
+developers/build/prebuilts/gradle/CustomNotifications/ samples/${PLATFORM_NAME}/ui/CustomNotifications
+developers/build/prebuilts/gradle/DoneBar/ samples/${PLATFORM_NAME}/ui/DoneBar
+developers/build/prebuilts/gradle/BasicImmersiveMode/ samples/${PLATFORM_NAME}/ui/BasicImmersiveMode
+developers/build/prebuilts/gradle/AdvancedImmersiveMode/ samples/${PLATFORM_NAME}/ui/AdvancedImmersiveMode
+developers/build/prebuilts/gradle/ImmersiveMode/ samples/${PLATFORM_NAME}/ui/ImmersiveMode
+developers/build/prebuilts/gradle/RepeatingAlarm/ samples/${PLATFORM_NAME}/background/RepeatingAlarm
+developers/build/prebuilts/gradle/TextLinkify/ samples/${PLATFORM_NAME}/ui/TextLinkify
+developers/build/prebuilts/gradle/BasicRenderScript samples/${PLATFORM_NAME}/renderscript/BasicRenderScript
+developers/build/prebuilts/gradle/RenderScriptIntrinsic samples/${PLATFORM_NAME}/renderscript/RenderScriptIntrinsic
+developers/build/prebuilts/gradle/SlidingTabsBasic samples/${PLATFORM_NAME}/ui/SlidingTabsBasic
+developers/build/prebuilts/gradle/SlidingTabsColors samples/${PLATFORM_NAME}/ui/SlidingTabsColors
+developers/build/prebuilts/gradle/CardEmulation samples/${PLATFORM_NAME}/connectivity/CardEmulation
+developers/build/prebuilts/gradle/CardReader samples/${PLATFORM_NAME}/connectivity/CardReader
+developers/build/prebuilts/gradle/BatchStepSensor samples/${PLATFORM_NAME}/sensors/BatchStepSensor
+developers/build/prebuilts/gradle/DisplayingBitmaps samples/${PLATFORM_NAME}/ui/DisplayingBitmaps
+developers/build/prebuilts/gradle/BasicTransition samples/${PLATFORM_NAME}/ui/BasicTransition
+developers/build/prebuilts/gradle/AdapterTransition samples/${PLATFORM_NAME}/ui/AdapterTransition
+developers/build/prebuilts/gradle/MediaRouter samples/${PLATFORM_NAME}/media/MediaRouter
+
+
# Old sample tree
development/samples/AccelerometerPlay samples/${PLATFORM_NAME}/legacy/AccelerometerPlay
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index d7bda3125..f202d595d 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -2469,6 +2469,16 @@
+
+{@link android.transition.Transition Transition} cannot be directly applied to
+{@link android.widget.AdapterView AdapterView}s. This sample demonstrates how to
+create a overlay layout and how to run a transition on it.
+
+ * For devices with displays with a width of 720dp or greater, the sample log is always visible,
+ * on other devices it's visibility is controlled by an item on the Action Bar.
+ */
+public class MainActivity extends SampleActivityBase {
+
+ public static final String TAG = "MainActivity";
+
+ // Whether the Log Fragment is currently shown
+ private boolean mLogShown;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ AdapterTransitionFragment fragment = new AdapterTransitionFragment();
+ transaction.replace(R.id.sample_content_fragment, fragment);
+ transaction.commit();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
+ logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
+ logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
+
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch(item.getItemId()) {
+ case R.id.menu_toggle_log:
+ mLogShown = !mLogShown;
+ ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
+ if (mLogShown) {
+ output.setDisplayedChild(1);
+ } else {
+ output.setDisplayedChild(0);
+ }
+ supportInvalidateOptionsMenu();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /** Create a chain of targets that will receive log data */
+ @Override
+ public void initializeLogging() {
+ // Wraps Android's native log framework.
+ LogWrapper logWrapper = new LogWrapper();
+ // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
+ Log.setLogNode(logWrapper);
+
+ // Filter strips out everything except the message text.
+ MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
+ logWrapper.setNext(msgFilter);
+
+ // On screen logging via a fragment with a TextView.
+ LogFragment logFragment = (LogFragment) getSupportFragmentManager()
+ .findFragmentById(R.id.log_fragment);
+ msgFilter.setNext(logFragment.getLogView());
+
+ Log.i(TAG, "Ready");
+ }
+}
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/Meat.java b/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/Meat.java
new file mode 100644
index 000000000..bca1c5f00
--- /dev/null
+++ b/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/Meat.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 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.adaptertransition;
+
+/**
+ * Sample data.
+ */
+public class Meat {
+
+ public int resourceId;
+ public String title;
+
+ public Meat(int resourceId, String title) {
+ this.resourceId = resourceId;
+ this.title = title;
+ }
+
+ public static final Meat[] MEATS = {
+ new Meat(R.drawable.p1, "First"),
+ new Meat(R.drawable.p2, "Second"),
+ new Meat(R.drawable.p3, "Third"),
+ new Meat(R.drawable.p4, "Fourth"),
+ new Meat(R.drawable.p5, "Fifth"),
+ new Meat(R.drawable.p6, "Sixth"),
+ new Meat(R.drawable.p7, "Seventh"),
+ new Meat(R.drawable.p8, "Eighth"),
+ new Meat(R.drawable.p9, "Ninth"),
+ new Meat(R.drawable.p10, "Tenth"),
+ new Meat(R.drawable.p11, "Eleventh"),
+ };
+
+}
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MeatAdapter.java b/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MeatAdapter.java
new file mode 100644
index 000000000..c7630cedf
--- /dev/null
+++ b/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MeatAdapter.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014 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.adaptertransition;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * This class provides data as Views. It is designed to support both ListView and GridView by
+ * changing a layout resource file to inflate.
+ */
+public class MeatAdapter extends BaseAdapter {
+
+ private final LayoutInflater mLayoutInflater;
+ private final int mResourceId;
+
+ /**
+ * Create a new instance of {@link MeatAdapter}.
+ *
+ * @param inflater The layout inflater.
+ * @param resourceId The resource ID for the layout to be used. The layout should contain an
+ * ImageView with ID of "meat_image" and a TextView with ID of "meat_title".
+ */
+ public MeatAdapter(LayoutInflater inflater, int resourceId) {
+ mLayoutInflater = inflater;
+ mResourceId = resourceId;
+ }
+
+ @Override
+ public int getCount() {
+ return Meat.MEATS.length;
+ }
+
+ @Override
+ public Meat getItem(int position) {
+ return Meat.MEATS[position];
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return Meat.MEATS[position].resourceId;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final View view;
+ final ViewHolder holder;
+ if (null == convertView) {
+ view = mLayoutInflater.inflate(mResourceId, parent, false);
+ holder = new ViewHolder();
+ assert view != null;
+ holder.image = (ImageView) view.findViewById(R.id.meat_image);
+ holder.title = (TextView) view.findViewById(R.id.meat_title);
+ view.setTag(holder);
+ } else {
+ view = convertView;
+ holder = (ViewHolder) view.getTag();
+ }
+ Meat meat = getItem(position);
+ holder.image.setImageResource(meat.resourceId);
+ holder.title.setText(meat.title);
+ return view;
+ }
+
+ private static class ViewHolder {
+ public ImageView image;
+ public TextView title;
+ }
+
+}
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/AdapterTransition/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
rename from samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java
rename to samples/browseable/AdapterTransition/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/AdapterTransition/src/com.example.android.common/logger/Log.java
similarity index 100%
rename from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java
rename to samples/browseable/AdapterTransition/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
rename from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java
rename to samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogNode.java
similarity index 100%
rename from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java
rename to samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogView.java
similarity index 100%
rename from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java
rename to samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
rename from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java
rename to samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/AdapterTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
rename from samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java
rename to samples/browseable/AdapterTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabLayout.java b/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabLayout.java
new file mode 100644
index 000000000..20049e335
--- /dev/null
+++ b/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabLayout.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.common.view;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.HorizontalScrollView;
+import android.widget.TextView;
+
+/**
+ * To be used with ViewPager to provide a tab indicator component which give constant feedback as to
+ * the user's scroll progress.
+ *
+ * To use the component, simply add it to your view hierarchy. Then in your
+ * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
+ * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
+ *
+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors
+ * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
+ * alternative is via the {@link TabColorizer} interface which provides you complete control over
+ * which color is used for any individual position.
+ *
+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
+ * providing the layout ID of your custom layout.
+ */
+public class SlidingTabLayout extends HorizontalScrollView {
+
+ /**
+ * Allows complete control over the colors drawn in the tab layout. Set with
+ * {@link #setCustomTabColorizer(TabColorizer)}.
+ */
+ public interface TabColorizer {
+
+ /**
+ * @return return the color of the indicator used when {@code position} is selected.
+ */
+ int getIndicatorColor(int position);
+
+ /**
+ * @return return the color of the divider drawn to the right of {@code position}.
+ */
+ int getDividerColor(int position);
+
+ }
+
+ private static final int TITLE_OFFSET_DIPS = 24;
+ private static final int TAB_VIEW_PADDING_DIPS = 16;
+ private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
+
+ private int mTitleOffset;
+
+ private int mTabViewLayoutId;
+ private int mTabViewTextViewId;
+
+ private ViewPager mViewPager;
+ private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
+
+ private final SlidingTabStrip mTabStrip;
+
+ public SlidingTabLayout(Context context) {
+ this(context, null);
+ }
+
+ public SlidingTabLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ // Disable the Scroll Bar
+ setHorizontalScrollBarEnabled(false);
+ // Make sure that the Tab Strips fills this View
+ setFillViewport(true);
+
+ mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
+
+ mTabStrip = new SlidingTabStrip(context);
+ addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ }
+
+ /**
+ * Set the custom {@link TabColorizer} to be used.
+ *
+ * If you only require simple custmisation then you can use
+ * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
+ * similar effects.
+ */
+ public void setCustomTabColorizer(TabColorizer tabColorizer) {
+ mTabStrip.setCustomTabColorizer(tabColorizer);
+ }
+
+ /**
+ * Sets the colors to be used for indicating the selected tab. These colors are treated as a
+ * circular array. Providing one color will mean that all tabs are indicated with the same color.
+ */
+ public void setSelectedIndicatorColors(int... colors) {
+ mTabStrip.setSelectedIndicatorColors(colors);
+ }
+
+ /**
+ * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
+ * Providing one color will mean that all tabs are indicated with the same color.
+ */
+ public void setDividerColors(int... colors) {
+ mTabStrip.setDividerColors(colors);
+ }
+
+ /**
+ * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
+ * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
+ * that the layout can update it's scroll position correctly.
+ *
+ * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
+ */
+ public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+ mViewPagerPageChangeListener = listener;
+ }
+
+ /**
+ * Set the custom layout to be inflated for the tab views.
+ *
+ * @param layoutResId Layout id to be inflated
+ * @param textViewId id of the {@link TextView} in the inflated view
+ */
+ public void setCustomTabView(int layoutResId, int textViewId) {
+ mTabViewLayoutId = layoutResId;
+ mTabViewTextViewId = textViewId;
+ }
+
+ /**
+ * Sets the associated view pager. Note that the assumption here is that the pager content
+ * (number of tabs and tab titles) does not change after this call has been made.
+ */
+ public void setViewPager(ViewPager viewPager) {
+ mTabStrip.removeAllViews();
+
+ mViewPager = viewPager;
+ if (viewPager != null) {
+ viewPager.setOnPageChangeListener(new InternalViewPagerListener());
+ populateTabStrip();
+ }
+ }
+
+ /**
+ * Create a default view to be used for tabs. This is called if a custom tab view is not set via
+ * {@link #setCustomTabView(int, int)}.
+ */
+ protected TextView createDefaultTabView(Context context) {
+ TextView textView = new TextView(context);
+ textView.setGravity(Gravity.CENTER);
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
+ textView.setTypeface(Typeface.DEFAULT_BOLD);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ // If we're running on Honeycomb or newer, then we can use the Theme's
+ // selectableItemBackground to ensure that the View has a pressed state
+ TypedValue outValue = new TypedValue();
+ getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
+ outValue, true);
+ textView.setBackgroundResource(outValue.resourceId);
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
+ textView.setAllCaps(true);
+ }
+
+ int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
+ textView.setPadding(padding, padding, padding, padding);
+
+ return textView;
+ }
+
+ private void populateTabStrip() {
+ final PagerAdapter adapter = mViewPager.getAdapter();
+ final View.OnClickListener tabClickListener = new TabClickListener();
+
+ for (int i = 0; i < adapter.getCount(); i++) {
+ View tabView = null;
+ TextView tabTitleView = null;
+
+ if (mTabViewLayoutId != 0) {
+ // If there is a custom tab view layout id set, try and inflate it
+ tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
+ false);
+ tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
+ }
+
+ if (tabView == null) {
+ tabView = createDefaultTabView(getContext());
+ }
+
+ if (tabTitleView == null && TextView.class.isInstance(tabView)) {
+ tabTitleView = (TextView) tabView;
+ }
+
+ tabTitleView.setText(adapter.getPageTitle(i));
+ tabView.setOnClickListener(tabClickListener);
+
+ mTabStrip.addView(tabView);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ if (mViewPager != null) {
+ scrollToTab(mViewPager.getCurrentItem(), 0);
+ }
+ }
+
+ private void scrollToTab(int tabIndex, int positionOffset) {
+ final int tabStripChildCount = mTabStrip.getChildCount();
+ if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
+ return;
+ }
+
+ View selectedChild = mTabStrip.getChildAt(tabIndex);
+ if (selectedChild != null) {
+ int targetScrollX = selectedChild.getLeft() + positionOffset;
+
+ if (tabIndex > 0 || positionOffset > 0) {
+ // If we're not at the first child and are mid-scroll, make sure we obey the offset
+ targetScrollX -= mTitleOffset;
+ }
+
+ scrollTo(targetScrollX, 0);
+ }
+ }
+
+ private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
+ private int mScrollState;
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ int tabStripChildCount = mTabStrip.getChildCount();
+ if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
+ return;
+ }
+
+ mTabStrip.onViewPagerPageChanged(position, positionOffset);
+
+ View selectedTitle = mTabStrip.getChildAt(position);
+ int extraOffset = (selectedTitle != null)
+ ? (int) (positionOffset * selectedTitle.getWidth())
+ : 0;
+ scrollToTab(position, extraOffset);
+
+ if (mViewPagerPageChangeListener != null) {
+ mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
+ positionOffsetPixels);
+ }
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ mScrollState = state;
+
+ if (mViewPagerPageChangeListener != null) {
+ mViewPagerPageChangeListener.onPageScrollStateChanged(state);
+ }
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+ mTabStrip.onViewPagerPageChanged(position, 0f);
+ scrollToTab(position, 0);
+ }
+
+ if (mViewPagerPageChangeListener != null) {
+ mViewPagerPageChangeListener.onPageSelected(position);
+ }
+ }
+
+ }
+
+ private class TabClickListener implements View.OnClickListener {
+ @Override
+ public void onClick(View v) {
+ for (int i = 0; i < mTabStrip.getChildCount(); i++) {
+ if (v == mTabStrip.getChildAt(i)) {
+ mViewPager.setCurrentItem(i);
+ return;
+ }
+ }
+ }
+ }
+
+}
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabStrip.java b/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabStrip.java
new file mode 100644
index 000000000..d5bbbae59
--- /dev/null
+++ b/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabStrip.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.common.view;
+
+import android.R;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+class SlidingTabStrip extends LinearLayout {
+
+ private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
+ private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
+ private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
+ private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
+
+ private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
+ private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
+ private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
+
+ private final int mBottomBorderThickness;
+ private final Paint mBottomBorderPaint;
+
+ private final int mSelectedIndicatorThickness;
+ private final Paint mSelectedIndicatorPaint;
+
+ private final int mDefaultBottomBorderColor;
+
+ private final Paint mDividerPaint;
+ private final float mDividerHeight;
+
+ private int mSelectedPosition;
+ private float mSelectionOffset;
+
+ private SlidingTabLayout.TabColorizer mCustomTabColorizer;
+ private final SimpleTabColorizer mDefaultTabColorizer;
+
+ SlidingTabStrip(Context context) {
+ this(context, null);
+ }
+
+ SlidingTabStrip(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setWillNotDraw(false);
+
+ final float density = getResources().getDisplayMetrics().density;
+
+ TypedValue outValue = new TypedValue();
+ context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
+ final int themeForegroundColor = outValue.data;
+
+ mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
+ DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
+
+ mDefaultTabColorizer = new SimpleTabColorizer();
+ mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
+ mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
+ DEFAULT_DIVIDER_COLOR_ALPHA));
+
+ mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
+ mBottomBorderPaint = new Paint();
+ mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
+
+ mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
+ mSelectedIndicatorPaint = new Paint();
+
+ mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
+ mDividerPaint = new Paint();
+ mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
+ }
+
+ void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
+ mCustomTabColorizer = customTabColorizer;
+ invalidate();
+ }
+
+ void setSelectedIndicatorColors(int... colors) {
+ // Make sure that the custom colorizer is removed
+ mCustomTabColorizer = null;
+ mDefaultTabColorizer.setIndicatorColors(colors);
+ invalidate();
+ }
+
+ void setDividerColors(int... colors) {
+ // Make sure that the custom colorizer is removed
+ mCustomTabColorizer = null;
+ mDefaultTabColorizer.setDividerColors(colors);
+ invalidate();
+ }
+
+ void onViewPagerPageChanged(int position, float positionOffset) {
+ mSelectedPosition = position;
+ mSelectionOffset = positionOffset;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ final int height = getHeight();
+ final int childCount = getChildCount();
+ final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
+ final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
+ ? mCustomTabColorizer
+ : mDefaultTabColorizer;
+
+ // Thick colored underline below the current selection
+ if (childCount > 0) {
+ View selectedTitle = getChildAt(mSelectedPosition);
+ int left = selectedTitle.getLeft();
+ int right = selectedTitle.getRight();
+ int color = tabColorizer.getIndicatorColor(mSelectedPosition);
+
+ if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
+ int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
+ if (color != nextColor) {
+ color = blendColors(nextColor, color, mSelectionOffset);
+ }
+
+ // Draw the selection partway between the tabs
+ View nextTitle = getChildAt(mSelectedPosition + 1);
+ left = (int) (mSelectionOffset * nextTitle.getLeft() +
+ (1.0f - mSelectionOffset) * left);
+ right = (int) (mSelectionOffset * nextTitle.getRight() +
+ (1.0f - mSelectionOffset) * right);
+ }
+
+ mSelectedIndicatorPaint.setColor(color);
+
+ canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
+ height, mSelectedIndicatorPaint);
+ }
+
+ // Thin underline along the entire bottom edge
+ canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
+
+ // Vertical separators between the titles
+ int separatorTop = (height - dividerHeightPx) / 2;
+ for (int i = 0; i < childCount - 1; i++) {
+ View child = getChildAt(i);
+ mDividerPaint.setColor(tabColorizer.getDividerColor(i));
+ canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
+ separatorTop + dividerHeightPx, mDividerPaint);
+ }
+ }
+
+ /**
+ * Set the alpha value of the {@code color} to be the given {@code alpha} value.
+ */
+ private static int setColorAlpha(int color, byte alpha) {
+ return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
+ }
+
+ /**
+ * Blend {@code color1} and {@code color2} using the given ratio.
+ *
+ * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
+ * 0.0 will return {@code color2}.
+ */
+ private static int blendColors(int color1, int color2, float ratio) {
+ final float inverseRation = 1f - ratio;
+ float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
+ float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
+ float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
+ return Color.rgb((int) r, (int) g, (int) b);
+ }
+
+ private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
+ private int[] mIndicatorColors;
+ private int[] mDividerColors;
+
+ @Override
+ public final int getIndicatorColor(int position) {
+ return mIndicatorColors[position % mIndicatorColors.length];
+ }
+
+ @Override
+ public final int getDividerColor(int position) {
+ return mDividerColors[position % mDividerColors.length];
+ }
+
+ void setIndicatorColors(int... colors) {
+ mIndicatorColors = colors;
+ }
+
+ void setDividerColors(int... colors) {
+ mDividerColors = colors;
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/browseable/AdvancedImmersiveMode/_index.jd b/samples/browseable/AdvancedImmersiveMode/_index.jd
index 2b19c9c74..7048fe838 100644
--- a/samples/browseable/AdvancedImmersiveMode/_index.jd
+++ b/samples/browseable/AdvancedImmersiveMode/_index.jd
@@ -5,10 +5,15 @@ page.tags="AdvancedImmersiveMode"
sample.group=UI
@jd:body
- Android 4.4 introduces a way for you to provide a more immersive screen
-experience in your app, by letting users show or hide the status bar and
-navigation bar with a swipe. This sample demonstrates how this features interacts with some of the other
+
+Android 4.4 introduces a way for you to provide a more immersive screen
+experience in your app by letting users show or hide the status bar and
+the navigation bar with a swipe.
+
+This sample demonstrates how this feature interacts with some of the other
UI flags related to full-screen apps. The sample also shows how to implement a
"sticky" mode, which re-hides the bars a few seconds after the user swipes
-them back in.
+ * For devices with displays with a width of 720dp or greater, the sample log is always visible,
+ * on other devices it's visibility is controlled by an item on the Action Bar.
*/
public class MainActivity extends SampleActivityBase {
public static final String TAG = "MainActivity";
- public static final String FRAGTAG = "AdvancedImmersiveModeFragment";
+ // Whether the Log Fragment is currently shown
+ private boolean mLogShown;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
- if (getSupportFragmentManager().findFragmentByTag(FRAGTAG) == null ) {
- FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- AdvancedImmersiveModeFragment fragment = new AdvancedImmersiveModeFragment();
- transaction.add(fragment, FRAGTAG);
- transaction.commit();
- }
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ AdvancedImmersiveModeFragment fragment = new AdvancedImmersiveModeFragment();
+ transaction.replace(R.id.sample_content_fragment, fragment);
+ transaction.commit();
}
@Override
@@ -58,6 +62,32 @@ public class MainActivity extends SampleActivityBase {
return true;
}
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
+ logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
+ logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
+
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch(item.getItemId()) {
+ case R.id.menu_toggle_log:
+ mLogShown = !mLogShown;
+ ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
+ if (mLogShown) {
+ output.setDisplayedChild(1);
+ } else {
+ output.setDisplayedChild(0);
+ }
+ supportInvalidateOptionsMenu();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
/** Create a chain of targets that will receive log data */
@Override
public void initializeLogging() {
diff --git a/samples/browseable/BasicGestureDetect/res/values-sw600dp/dimens.xml b/samples/browseable/AppRestrictions/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicGestureDetect/res/values-sw600dp/dimens.xml
rename to samples/browseable/AppRestrictions/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/BasicGestureDetect/res/values-sw600dp/styles.xml b/samples/browseable/AppRestrictions/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/BasicGestureDetect/res/values-sw600dp/styles.xml
rename to samples/browseable/AppRestrictions/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/BasicMediaDecoder/res/values/dimens.xml b/samples/browseable/AppRestrictions/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicMediaDecoder/res/values/dimens.xml
rename to samples/browseable/AppRestrictions/res/values/template-dimens.xml
diff --git a/samples/browseable/BluetoothLeGatt/res/values/styles.xml b/samples/browseable/AppRestrictions/res/values/template-styles.xml
similarity index 100%
rename from samples/browseable/BluetoothLeGatt/res/values/styles.xml
rename to samples/browseable/AppRestrictions/res/values/template-styles.xml
diff --git a/samples/browseable/BasicImmersiveMode/res/values-sw600dp/dimens.xml b/samples/browseable/BasicAccessibility/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/BasicImmersiveMode/res/values-sw600dp/dimens.xml
rename to samples/browseable/BasicAccessibility/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/BasicImmersiveMode/res/values-sw600dp/styles.xml b/samples/browseable/BasicAccessibility/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/BasicImmersiveMode/res/values-sw600dp/styles.xml
rename to samples/browseable/BasicAccessibility/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/BasicAccessibility/res/values/dimens.xml b/samples/browseable/BasicAccessibility/res/values/dimens.xml
index 39e710b5c..a5e4ebef3 100644
--- a/samples/browseable/BasicAccessibility/res/values/dimens.xml
+++ b/samples/browseable/BasicAccessibility/res/values/dimens.xml
@@ -1,32 +1,20 @@
+ 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.
+-->
+ This sample demonstrates the basic steps for using RenderScript. In this + example, the app uses RenderScript to + perform graphical filter operations on a image. +
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicRenderScript/res/drawable-hdpi/ic_launcher.png old mode 100644 new mode 100755 similarity index 100% rename from samples/training/bitmapfun/BitmapFun/src/main/res/drawable-hdpi/ic_launcher.png rename to samples/browseable/BasicRenderScript/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/BasicRenderScript/res/drawable-hdpi/tile.9.png b/samples/browseable/BasicRenderScript/res/drawable-hdpi/tile.9.png new file mode 100644 index 000000000..135862883 Binary files /dev/null and b/samples/browseable/BasicRenderScript/res/drawable-hdpi/tile.9.png differ diff --git a/samples/browseable/BasicRenderScript/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicRenderScript/res/drawable-mdpi/ic_launcher.png new file mode 100755 index 000000000..4ccd98e09 Binary files /dev/null and b/samples/browseable/BasicRenderScript/res/drawable-mdpi/ic_launcher.png differ diff --git a/samples/browseable/BasicRenderScript/res/drawable-nodpi/data.jpg b/samples/browseable/BasicRenderScript/res/drawable-nodpi/data.jpg new file mode 100644 index 000000000..81a87b172 Binary files /dev/null and b/samples/browseable/BasicRenderScript/res/drawable-nodpi/data.jpg differ diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicRenderScript/res/drawable-xhdpi/ic_launcher.png old mode 100644 new mode 100755 similarity index 100% rename from samples/training/bitmapfun/BitmapFun/src/main/res/drawable-xhdpi/ic_launcher.png rename to samples/browseable/BasicRenderScript/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/BasicRenderScript/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicRenderScript/res/drawable-xxhdpi/ic_launcher.png new file mode 100755 index 000000000..3c45f511e Binary files /dev/null and b/samples/browseable/BasicRenderScript/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/samples/browseable/BasicRenderScript/res/layout/activity_main.xml b/samples/browseable/BasicRenderScript/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/BasicRenderScript/res/layout/activity_main.xml @@ -0,0 +1,36 @@ + + ++This sample demonstrates the basic use of the transition framework introduced in KitKat. +Select each of the radio buttons to switch between the +{@link android.transition.Scene Scene}s. +
diff --git a/samples/browseable/BasicTransition/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicTransition/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 000000000..0f5d36076 Binary files /dev/null and b/samples/browseable/BasicTransition/res/drawable-hdpi/ic_launcher.png differ diff --git a/samples/browseable/BasicTransition/res/drawable-hdpi/tile.9.png b/samples/browseable/BasicTransition/res/drawable-hdpi/tile.9.png new file mode 100644 index 000000000..135862883 Binary files /dev/null and b/samples/browseable/BasicTransition/res/drawable-hdpi/tile.9.png differ diff --git a/samples/browseable/BasicTransition/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicTransition/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 000000000..72d85c944 Binary files /dev/null and b/samples/browseable/BasicTransition/res/drawable-mdpi/ic_launcher.png differ diff --git a/samples/browseable/BasicTransition/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicTransition/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 000000000..cf93e6994 Binary files /dev/null and b/samples/browseable/BasicTransition/res/drawable-xhdpi/ic_launcher.png differ diff --git a/samples/browseable/BasicTransition/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicTransition/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..149a98491 Binary files /dev/null and b/samples/browseable/BasicTransition/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/samples/browseable/BasicTransition/res/drawable/oval.xml b/samples/browseable/BasicTransition/res/drawable/oval.xml new file mode 100644 index 000000000..07f3abd9e --- /dev/null +++ b/samples/browseable/BasicTransition/res/drawable/oval.xml @@ -0,0 +1,20 @@ + + ++ * For devices with displays with a width of 720dp or greater, the sample log is always visible, + * on other devices it's visibility is controlled by an item on the Action Bar. + */ +public class MainActivity extends SampleActivityBase { + + public static final String TAG = "MainActivity"; + + // Whether the Log Fragment is currently shown + private boolean mLogShown; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + BasicTransitionFragment fragment = new BasicTransitionFragment(); + transaction.replace(R.id.sample_content_fragment, fragment); + transaction.commit(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + MenuItem logToggle = menu.findItem(R.id.menu_toggle_log); + logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator); + logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log); + + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch(item.getItemId()) { + case R.id.menu_toggle_log: + mLogShown = !mLogShown; + ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output); + if (mLogShown) { + output.setDisplayedChild(1); + } else { + output.setDisplayedChild(0); + } + supportInvalidateOptionsMenu(); + return true; + } + return super.onOptionsItemSelected(item); + } + + /** Create a chain of targets that will receive log data */ + @Override + public void initializeLogging() { + // Wraps Android's native log framework. + LogWrapper logWrapper = new LogWrapper(); + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + Log.setLogNode(logWrapper); + + // Filter strips out everything except the message text. + MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); + logWrapper.setNext(msgFilter); + + // On screen logging via a fragment with a TextView. + LogFragment logFragment = (LogFragment) getSupportFragmentManager() + .findFragmentById(R.id.log_fragment); + msgFilter.setNext(logFragment.getLogView()); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/BasicTransition/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/BasicTransition/src/com.example.android.common/activities/SampleActivityBase.java new file mode 100644 index 000000000..3228927b7 --- /dev/null +++ b/samples/browseable/BasicTransition/src/com.example.android.common/activities/SampleActivityBase.java @@ -0,0 +1,52 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.example.android.common.activities; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogWrapper; + +/** + * Base launcher activity, to handle most of the common plumbing for samples. + */ +public class SampleActivityBase extends FragmentActivity { + + public static final String TAG = "SampleActivityBase"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + initializeLogging(); + } + + /** Set up targets to receive log data */ + public void initializeLogging() { + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/BasicTransition/src/com.example.android.common/logger/Log.java b/samples/browseable/BasicTransition/src/com.example.android.common/logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/BasicTransition/src/com.example.android.common/logger/Log.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + *
When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.
+ */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/BasicTransition/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/BasicTransition/src/com.example.android.common/logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/BasicTransition/src/com.example.android.common/logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +} \ No newline at end of file diff --git a/samples/browseable/BasicTransition/src/com.example.android.common/logger/LogNode.java b/samples/browseable/BasicTransition/src/com.example.android.common/logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/BasicTransition/src/com.example.android.common/logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/BasicTransition/src/com.example.android.common/logger/LogView.java b/samples/browseable/BasicTransition/src/com.example.android.common/logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/BasicTransition/src/com.example.android.common/logger/LogView.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/BasicTransition/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/BasicTransition/src/com.example.android.common/logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/BasicTransition/src/com.example.android.common/logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/BasicTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/BasicTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/BasicTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/BasicTransition/src/com.example.android.common/view/SlidingTabLayout.java b/samples/browseable/BasicTransition/src/com.example.android.common/view/SlidingTabLayout.java new file mode 100644 index 000000000..20049e335 --- /dev/null +++ b/samples/browseable/BasicTransition/src/com.example.android.common/view/SlidingTabLayout.java @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.view; + +import android.content.Context; +import android.graphics.Typeface; +import android.os.Build; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.HorizontalScrollView; +import android.widget.TextView; + +/** + * To be used with ViewPager to provide a tab indicator component which give constant feedback as to + * the user's scroll progress. + *+ * To use the component, simply add it to your view hierarchy. Then in your + * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call + * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for. + *
+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors + * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The + * alternative is via the {@link TabColorizer} interface which provides you complete control over + * which color is used for any individual position. + *
+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
+ * providing the layout ID of your custom layout.
+ */
+public class SlidingTabLayout extends HorizontalScrollView {
+
+ /**
+ * Allows complete control over the colors drawn in the tab layout. Set with
+ * {@link #setCustomTabColorizer(TabColorizer)}.
+ */
+ public interface TabColorizer {
+
+ /**
+ * @return return the color of the indicator used when {@code position} is selected.
+ */
+ int getIndicatorColor(int position);
+
+ /**
+ * @return return the color of the divider drawn to the right of {@code position}.
+ */
+ int getDividerColor(int position);
+
+ }
+
+ private static final int TITLE_OFFSET_DIPS = 24;
+ private static final int TAB_VIEW_PADDING_DIPS = 16;
+ private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
+
+ private int mTitleOffset;
+
+ private int mTabViewLayoutId;
+ private int mTabViewTextViewId;
+
+ private ViewPager mViewPager;
+ private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
+
+ private final SlidingTabStrip mTabStrip;
+
+ public SlidingTabLayout(Context context) {
+ this(context, null);
+ }
+
+ public SlidingTabLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ // Disable the Scroll Bar
+ setHorizontalScrollBarEnabled(false);
+ // Make sure that the Tab Strips fills this View
+ setFillViewport(true);
+
+ mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
+
+ mTabStrip = new SlidingTabStrip(context);
+ addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ }
+
+ /**
+ * Set the custom {@link TabColorizer} to be used.
+ *
+ * If you only require simple custmisation then you can use
+ * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
+ * similar effects.
+ */
+ public void setCustomTabColorizer(TabColorizer tabColorizer) {
+ mTabStrip.setCustomTabColorizer(tabColorizer);
+ }
+
+ /**
+ * Sets the colors to be used for indicating the selected tab. These colors are treated as a
+ * circular array. Providing one color will mean that all tabs are indicated with the same color.
+ */
+ public void setSelectedIndicatorColors(int... colors) {
+ mTabStrip.setSelectedIndicatorColors(colors);
+ }
+
+ /**
+ * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
+ * Providing one color will mean that all tabs are indicated with the same color.
+ */
+ public void setDividerColors(int... colors) {
+ mTabStrip.setDividerColors(colors);
+ }
+
+ /**
+ * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
+ * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
+ * that the layout can update it's scroll position correctly.
+ *
+ * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
+ */
+ public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+ mViewPagerPageChangeListener = listener;
+ }
+
+ /**
+ * Set the custom layout to be inflated for the tab views.
+ *
+ * @param layoutResId Layout id to be inflated
+ * @param textViewId id of the {@link TextView} in the inflated view
+ */
+ public void setCustomTabView(int layoutResId, int textViewId) {
+ mTabViewLayoutId = layoutResId;
+ mTabViewTextViewId = textViewId;
+ }
+
+ /**
+ * Sets the associated view pager. Note that the assumption here is that the pager content
+ * (number of tabs and tab titles) does not change after this call has been made.
+ */
+ public void setViewPager(ViewPager viewPager) {
+ mTabStrip.removeAllViews();
+
+ mViewPager = viewPager;
+ if (viewPager != null) {
+ viewPager.setOnPageChangeListener(new InternalViewPagerListener());
+ populateTabStrip();
+ }
+ }
+
+ /**
+ * Create a default view to be used for tabs. This is called if a custom tab view is not set via
+ * {@link #setCustomTabView(int, int)}.
+ */
+ protected TextView createDefaultTabView(Context context) {
+ TextView textView = new TextView(context);
+ textView.setGravity(Gravity.CENTER);
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
+ textView.setTypeface(Typeface.DEFAULT_BOLD);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ // If we're running on Honeycomb or newer, then we can use the Theme's
+ // selectableItemBackground to ensure that the View has a pressed state
+ TypedValue outValue = new TypedValue();
+ getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
+ outValue, true);
+ textView.setBackgroundResource(outValue.resourceId);
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
+ textView.setAllCaps(true);
+ }
+
+ int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
+ textView.setPadding(padding, padding, padding, padding);
+
+ return textView;
+ }
+
+ private void populateTabStrip() {
+ final PagerAdapter adapter = mViewPager.getAdapter();
+ final View.OnClickListener tabClickListener = new TabClickListener();
+
+ for (int i = 0; i < adapter.getCount(); i++) {
+ View tabView = null;
+ TextView tabTitleView = null;
+
+ if (mTabViewLayoutId != 0) {
+ // If there is a custom tab view layout id set, try and inflate it
+ tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
+ false);
+ tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
+ }
+
+ if (tabView == null) {
+ tabView = createDefaultTabView(getContext());
+ }
+
+ if (tabTitleView == null && TextView.class.isInstance(tabView)) {
+ tabTitleView = (TextView) tabView;
+ }
+
+ tabTitleView.setText(adapter.getPageTitle(i));
+ tabView.setOnClickListener(tabClickListener);
+
+ mTabStrip.addView(tabView);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ if (mViewPager != null) {
+ scrollToTab(mViewPager.getCurrentItem(), 0);
+ }
+ }
+
+ private void scrollToTab(int tabIndex, int positionOffset) {
+ final int tabStripChildCount = mTabStrip.getChildCount();
+ if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
+ return;
+ }
+
+ View selectedChild = mTabStrip.getChildAt(tabIndex);
+ if (selectedChild != null) {
+ int targetScrollX = selectedChild.getLeft() + positionOffset;
+
+ if (tabIndex > 0 || positionOffset > 0) {
+ // If we're not at the first child and are mid-scroll, make sure we obey the offset
+ targetScrollX -= mTitleOffset;
+ }
+
+ scrollTo(targetScrollX, 0);
+ }
+ }
+
+ private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
+ private int mScrollState;
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ int tabStripChildCount = mTabStrip.getChildCount();
+ if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
+ return;
+ }
+
+ mTabStrip.onViewPagerPageChanged(position, positionOffset);
+
+ View selectedTitle = mTabStrip.getChildAt(position);
+ int extraOffset = (selectedTitle != null)
+ ? (int) (positionOffset * selectedTitle.getWidth())
+ : 0;
+ scrollToTab(position, extraOffset);
+
+ if (mViewPagerPageChangeListener != null) {
+ mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
+ positionOffsetPixels);
+ }
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ mScrollState = state;
+
+ if (mViewPagerPageChangeListener != null) {
+ mViewPagerPageChangeListener.onPageScrollStateChanged(state);
+ }
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+ mTabStrip.onViewPagerPageChanged(position, 0f);
+ scrollToTab(position, 0);
+ }
+
+ if (mViewPagerPageChangeListener != null) {
+ mViewPagerPageChangeListener.onPageSelected(position);
+ }
+ }
+
+ }
+
+ private class TabClickListener implements View.OnClickListener {
+ @Override
+ public void onClick(View v) {
+ for (int i = 0; i < mTabStrip.getChildCount(); i++) {
+ if (v == mTabStrip.getChildAt(i)) {
+ mViewPager.setCurrentItem(i);
+ return;
+ }
+ }
+ }
+ }
+
+}
diff --git a/samples/browseable/BasicTransition/src/com.example.android.common/view/SlidingTabStrip.java b/samples/browseable/BasicTransition/src/com.example.android.common/view/SlidingTabStrip.java
new file mode 100644
index 000000000..d5bbbae59
--- /dev/null
+++ b/samples/browseable/BasicTransition/src/com.example.android.common/view/SlidingTabStrip.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.common.view;
+
+import android.R;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+class SlidingTabStrip extends LinearLayout {
+
+ private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
+ private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
+ private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
+ private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
+
+ private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
+ private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
+ private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
+
+ private final int mBottomBorderThickness;
+ private final Paint mBottomBorderPaint;
+
+ private final int mSelectedIndicatorThickness;
+ private final Paint mSelectedIndicatorPaint;
+
+ private final int mDefaultBottomBorderColor;
+
+ private final Paint mDividerPaint;
+ private final float mDividerHeight;
+
+ private int mSelectedPosition;
+ private float mSelectionOffset;
+
+ private SlidingTabLayout.TabColorizer mCustomTabColorizer;
+ private final SimpleTabColorizer mDefaultTabColorizer;
+
+ SlidingTabStrip(Context context) {
+ this(context, null);
+ }
+
+ SlidingTabStrip(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setWillNotDraw(false);
+
+ final float density = getResources().getDisplayMetrics().density;
+
+ TypedValue outValue = new TypedValue();
+ context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
+ final int themeForegroundColor = outValue.data;
+
+ mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
+ DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
+
+ mDefaultTabColorizer = new SimpleTabColorizer();
+ mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
+ mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
+ DEFAULT_DIVIDER_COLOR_ALPHA));
+
+ mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
+ mBottomBorderPaint = new Paint();
+ mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
+
+ mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
+ mSelectedIndicatorPaint = new Paint();
+
+ mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
+ mDividerPaint = new Paint();
+ mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
+ }
+
+ void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
+ mCustomTabColorizer = customTabColorizer;
+ invalidate();
+ }
+
+ void setSelectedIndicatorColors(int... colors) {
+ // Make sure that the custom colorizer is removed
+ mCustomTabColorizer = null;
+ mDefaultTabColorizer.setIndicatorColors(colors);
+ invalidate();
+ }
+
+ void setDividerColors(int... colors) {
+ // Make sure that the custom colorizer is removed
+ mCustomTabColorizer = null;
+ mDefaultTabColorizer.setDividerColors(colors);
+ invalidate();
+ }
+
+ void onViewPagerPageChanged(int position, float positionOffset) {
+ mSelectedPosition = position;
+ mSelectionOffset = positionOffset;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ final int height = getHeight();
+ final int childCount = getChildCount();
+ final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
+ final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
+ ? mCustomTabColorizer
+ : mDefaultTabColorizer;
+
+ // Thick colored underline below the current selection
+ if (childCount > 0) {
+ View selectedTitle = getChildAt(mSelectedPosition);
+ int left = selectedTitle.getLeft();
+ int right = selectedTitle.getRight();
+ int color = tabColorizer.getIndicatorColor(mSelectedPosition);
+
+ if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
+ int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
+ if (color != nextColor) {
+ color = blendColors(nextColor, color, mSelectionOffset);
+ }
+
+ // Draw the selection partway between the tabs
+ View nextTitle = getChildAt(mSelectedPosition + 1);
+ left = (int) (mSelectionOffset * nextTitle.getLeft() +
+ (1.0f - mSelectionOffset) * left);
+ right = (int) (mSelectionOffset * nextTitle.getRight() +
+ (1.0f - mSelectionOffset) * right);
+ }
+
+ mSelectedIndicatorPaint.setColor(color);
+
+ canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
+ height, mSelectedIndicatorPaint);
+ }
+
+ // Thin underline along the entire bottom edge
+ canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
+
+ // Vertical separators between the titles
+ int separatorTop = (height - dividerHeightPx) / 2;
+ for (int i = 0; i < childCount - 1; i++) {
+ View child = getChildAt(i);
+ mDividerPaint.setColor(tabColorizer.getDividerColor(i));
+ canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
+ separatorTop + dividerHeightPx, mDividerPaint);
+ }
+ }
+
+ /**
+ * Set the alpha value of the {@code color} to be the given {@code alpha} value.
+ */
+ private static int setColorAlpha(int color, byte alpha) {
+ return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
+ }
+
+ /**
+ * Blend {@code color1} and {@code color2} using the given ratio.
+ *
+ * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
+ * 0.0 will return {@code color2}.
+ */
+ private static int blendColors(int color1, int color2, float ratio) {
+ final float inverseRation = 1f - ratio;
+ float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
+ float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
+ float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
+ return Color.rgb((int) r, (int) g, (int) b);
+ }
+
+ private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
+ private int[] mIndicatorColors;
+ private int[] mDividerColors;
+
+ @Override
+ public final int getIndicatorColor(int position) {
+ return mIndicatorColors[position % mIndicatorColors.length];
+ }
+
+ @Override
+ public final int getDividerColor(int position) {
+ return mDividerColors[position % mDividerColors.length];
+ }
+
+ void setIndicatorColors(int... colors) {
+ mIndicatorColors = colors;
+ }
+
+ void setDividerColors(int... colors) {
+ mDividerColors = colors;
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/browseable/BatchStepSensor/AndroidManifest.xml b/samples/browseable/BatchStepSensor/AndroidManifest.xml
new file mode 100644
index 000000000..2c4e4f296
--- /dev/null
+++ b/samples/browseable/BatchStepSensor/AndroidManifest.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+ This sample demonstrates the use of the two step sensors (step detector and + counter) and sensor batching. It shows how to register a {@link + android.hardware.SensorEventListener} with and without batching and shows how + these events are received. +
+ ++ The Step Detector sensor fires an event when a step is detected, while the + step counter returns the total number of steps since a listener was first + registered for this sensor. Both sensors only count steps while a listener is + registered. +
+ ++ This sample only covers the basic case, where a listener is only registered + while the app is running. Likewise, batched sensors can be used in the + background (when the CPU is suspended), which requires manually flushing the + sensor event queue before it overflows, which is not covered in this sample. +
diff --git a/samples/browseable/BatchStepSensor/res/drawable-hdpi/ic_action_cancel.png b/samples/browseable/BatchStepSensor/res/drawable-hdpi/ic_action_cancel.png new file mode 100644 index 000000000..f889617e4 Binary files /dev/null and b/samples/browseable/BatchStepSensor/res/drawable-hdpi/ic_action_cancel.png differ diff --git a/samples/browseable/BatchStepSensor/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BatchStepSensor/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 000000000..564742cce Binary files /dev/null and b/samples/browseable/BatchStepSensor/res/drawable-hdpi/ic_launcher.png differ diff --git a/samples/browseable/BatchStepSensor/res/drawable-hdpi/tile.9.png b/samples/browseable/BatchStepSensor/res/drawable-hdpi/tile.9.png new file mode 100644 index 000000000..135862883 Binary files /dev/null and b/samples/browseable/BatchStepSensor/res/drawable-hdpi/tile.9.png differ diff --git a/samples/browseable/BatchStepSensor/res/drawable-mdpi/ic_action_cancel.png b/samples/browseable/BatchStepSensor/res/drawable-mdpi/ic_action_cancel.png new file mode 100644 index 000000000..d5a9384da Binary files /dev/null and b/samples/browseable/BatchStepSensor/res/drawable-mdpi/ic_action_cancel.png differ diff --git a/samples/browseable/BatchStepSensor/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BatchStepSensor/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 000000000..08abe5759 Binary files /dev/null and b/samples/browseable/BatchStepSensor/res/drawable-mdpi/ic_launcher.png differ diff --git a/samples/browseable/BatchStepSensor/res/drawable-xhdpi/card_bg.9.png b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/card_bg.9.png new file mode 100644 index 000000000..23b30c15c Binary files /dev/null and b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/card_bg.9.png differ diff --git a/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_negative.png b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_negative.png new file mode 100644 index 000000000..1e6a64eb7 Binary files /dev/null and b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_negative.png differ diff --git a/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_negative_pressed.png b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_negative_pressed.png new file mode 100644 index 000000000..13c105e4e Binary files /dev/null and b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_negative_pressed.png differ diff --git a/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_neutral.png b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_neutral.png new file mode 100644 index 000000000..f8874cdd4 Binary files /dev/null and b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_neutral.png differ diff --git a/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_neutral_pressed.png b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_neutral_pressed.png new file mode 100644 index 000000000..a8f5e676e Binary files /dev/null and b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_neutral_pressed.png differ diff --git a/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_positive.png b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_positive.png new file mode 100644 index 000000000..b0a68c3a7 Binary files /dev/null and b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_positive.png differ diff --git a/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_positive_pressed.png b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_positive_pressed.png new file mode 100644 index 000000000..528b5aaae Binary files /dev/null and b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_cardaction_positive_pressed.png differ diff --git a/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 000000000..15bafae90 Binary files /dev/null and b/samples/browseable/BatchStepSensor/res/drawable-xhdpi/ic_launcher.png differ diff --git a/samples/browseable/BatchStepSensor/res/drawable-xxhdpi/ic_action_cancel.png b/samples/browseable/BatchStepSensor/res/drawable-xxhdpi/ic_action_cancel.png new file mode 100644 index 000000000..331c545b8 Binary files /dev/null and b/samples/browseable/BatchStepSensor/res/drawable-xxhdpi/ic_action_cancel.png differ diff --git a/samples/browseable/BatchStepSensor/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BatchStepSensor/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..40bdd35da Binary files /dev/null and b/samples/browseable/BatchStepSensor/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/samples/browseable/BatchStepSensor/res/drawable/card_action_bg.xml b/samples/browseable/BatchStepSensor/res/drawable/card_action_bg.xml new file mode 100644 index 000000000..f86cf2fc5 --- /dev/null +++ b/samples/browseable/BatchStepSensor/res/drawable/card_action_bg.xml @@ -0,0 +1,7 @@ + + +When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.
+ */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +} \ No newline at end of file diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogNode.java b/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogView.java b/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogView.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/BatchStepSensor/src/com.example.android.common/logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/ListPopupMenu/res/values-sw600dp/dimens.xml b/samples/browseable/BluetoothLeGatt/res/values-sw600dp/template-dimens.xml similarity index 100% rename from samples/browseable/ListPopupMenu/res/values-sw600dp/dimens.xml rename to samples/browseable/BluetoothLeGatt/res/values-sw600dp/template-dimens.xml diff --git a/samples/browseable/ListPopupMenu/res/values-sw600dp/styles.xml b/samples/browseable/BluetoothLeGatt/res/values-sw600dp/template-styles.xml similarity index 100% rename from samples/browseable/ListPopupMenu/res/values-sw600dp/styles.xml rename to samples/browseable/BluetoothLeGatt/res/values-sw600dp/template-styles.xml diff --git a/samples/browseable/BluetoothLeGatt/res/values/template-dimens.xml b/samples/browseable/BluetoothLeGatt/res/values/template-dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/values/template-dimens.xml @@ -0,0 +1,32 @@ + + ++ This sample demonstrates how to emulate an NFC card, using the Host Card Emulation + feature added in Android 4.4. This sample makes the device appear as a + loyalty card whenever the screen is on and the user taps their device on an + appropriately configured NFC reader. +
+ ++ The CardReader sample + can be used to read the loyalty card implemented in this sample. +
diff --git a/samples/browseable/CardEmulation/res/drawable-hdpi/ic_launcher.png b/samples/browseable/CardEmulation/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 000000000..0be8f85e2 Binary files /dev/null and b/samples/browseable/CardEmulation/res/drawable-hdpi/ic_launcher.png differ diff --git a/samples/browseable/CardEmulation/res/drawable-hdpi/tile.9.png b/samples/browseable/CardEmulation/res/drawable-hdpi/tile.9.png new file mode 100644 index 000000000..135862883 Binary files /dev/null and b/samples/browseable/CardEmulation/res/drawable-hdpi/tile.9.png differ diff --git a/samples/browseable/CardEmulation/res/drawable-mdpi/ic_launcher.png b/samples/browseable/CardEmulation/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 000000000..eb13405ad Binary files /dev/null and b/samples/browseable/CardEmulation/res/drawable-mdpi/ic_launcher.png differ diff --git a/samples/browseable/CardEmulation/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/CardEmulation/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 000000000..8d1e6a6af Binary files /dev/null and b/samples/browseable/CardEmulation/res/drawable-xhdpi/ic_launcher.png differ diff --git a/samples/browseable/CardEmulation/res/drawable-xxhdpi/card_background.png b/samples/browseable/CardEmulation/res/drawable-xxhdpi/card_background.png new file mode 100644 index 000000000..86a7ba764 Binary files /dev/null and b/samples/browseable/CardEmulation/res/drawable-xxhdpi/card_background.png differ diff --git a/samples/browseable/CardEmulation/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/CardEmulation/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..7ea109fce Binary files /dev/null and b/samples/browseable/CardEmulation/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/samples/browseable/CardEmulation/res/layout-w720dp/activity_main.xml b/samples/browseable/CardEmulation/res/layout-w720dp/activity_main.xml new file mode 100755 index 000000000..c9a52f621 --- /dev/null +++ b/samples/browseable/CardEmulation/res/layout-w720dp/activity_main.xml @@ -0,0 +1,73 @@ + +The default SharedPreferences instance is used as the backing storage. Values are cached + * in memory for performance. + * + *
This class is thread-safe. + */ +public class AccountStorage { + private static final String PREF_ACCOUNT_NUMBER = "account_number"; + private static final String DEFAULT_ACCOUNT_NUMBER = "00000000"; + private static final String TAG = "AccountStorage"; + private static String sAccount = null; + private static final Object sAccountLock = new Object(); + + public static void SetAccount(Context c, String s) { + synchronized(sAccountLock) { + Log.i(TAG, "Setting account number: " + s); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c); + prefs.edit().putString(PREF_ACCOUNT_NUMBER, s).commit(); + sAccount = s; + } + } + + public static String GetAccount(Context c) { + synchronized (sAccountLock) { + if (sAccount == null) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c); + String account = prefs.getString(PREF_ACCOUNT_NUMBER, DEFAULT_ACCOUNT_NUMBER); + sAccount = account; + } + return sAccount; + } + } +} diff --git a/samples/browseable/CardEmulation/src/com.example.android.cardemulation/CardEmulationFragment.java b/samples/browseable/CardEmulation/src/com.example.android.cardemulation/CardEmulationFragment.java new file mode 100644 index 000000000..f26efda26 --- /dev/null +++ b/samples/browseable/CardEmulation/src/com.example.android.cardemulation/CardEmulationFragment.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.cardemulation; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; + +/** + * Generic UI for sample discovery. + */ +public class CardEmulationFragment extends Fragment { + + public static final String TAG = "CardEmulationFragment"; + + /** Called when sample is created. Displays generic UI with welcome text. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + View v = inflater.inflate(R.layout.main_fragment, container, false); + EditText account = (EditText) v.findViewById(R.id.card_account_field); + account.setText(AccountStorage.GetAccount(getActivity())); + account.addTextChangedListener(new AccountUpdater()); + return v; + } + + + private class AccountUpdater implements TextWatcher { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // Not implemented. + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + // Not implemented. + } + + @Override + public void afterTextChanged(Editable s) { + String account = s.toString(); + AccountStorage.SetAccount(getActivity(), account); + } + } +} diff --git a/samples/browseable/CardEmulation/src/com.example.android.cardemulation/CardService.java b/samples/browseable/CardEmulation/src/com.example.android.cardemulation/CardService.java new file mode 100644 index 000000000..a409d28f4 --- /dev/null +++ b/samples/browseable/CardEmulation/src/com.example.android.cardemulation/CardService.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.cardemulation; + +import android.nfc.cardemulation.HostApduService; +import android.os.Bundle; +import com.example.android.common.logger.Log; + +import java.util.Arrays; + +/** + * This is a sample APDU Service which demonstrates how to interface with the card emulation support + * added in Android 4.4, KitKat. + * + *
This sample replies to any requests sent with the string "Hello World". In real-world + * situations, you would need to modify this code to implement your desired communication + * protocol. + * + *
This sample will be invoked for any terminals selecting AIDs of 0xF11111111, 0xF22222222, or + * 0xF33333333. See src/main/res/xml/aid_list.xml for more details. + * + *
Note: This is a low-level interface. Unlike the NdefMessage many developers + * are familiar with for implementing Android Beam in apps, card emulation only provides a + * byte-array based communication channel. It is left to developers to implement higher level + * protocol support as needed. + */ +public class CardService extends HostApduService { + private static final String TAG = "CardService"; + // AID for our loyalty card service. + private static final String SAMPLE_LOYALTY_CARD_AID = "F222222222"; + // ISO-DEP command HEADER for selecting an AID. + // Format: [Class | Instruction | Parameter 1 | Parameter 2] + private static final String SELECT_APDU_HEADER = "00A40400"; + // "OK" status word sent in response to SELECT AID command (0x9000) + private static final byte[] SELECT_OK_SW = HexStringToByteArray("9000"); + // "UNKNOWN" status word sent in response to invalid APDU command (0x0000) + private static final byte[] UNKNOWN_CMD_SW = HexStringToByteArray("0000"); + private static final byte[] SELECT_APDU = BuildSelectApdu(SAMPLE_LOYALTY_CARD_AID); + + /** + * Called if the connection to the NFC card is lost, in order to let the application know the + * cause for the disconnection (either a lost link, or another AID being selected by the + * reader). + * + * @param reason Either DEACTIVATION_LINK_LOSS or DEACTIVATION_DESELECTED + */ + @Override + public void onDeactivated(int reason) { } + + /** + * This method will be called when a command APDU has been received from a remote device. A + * response APDU can be provided directly by returning a byte-array in this method. In general + * response APDUs must be sent as quickly as possible, given the fact that the user is likely + * holding his device over an NFC reader when this method is called. + * + *
If there are multiple services that have registered for the same AIDs in + * their meta-data entry, you will only get called if the user has explicitly selected your + * service, either as a default or just for the next tap. + * + *
This method is running on the main thread of your application. If you + * cannot return a response APDU immediately, return null and use the {@link + * #sendResponseApdu(byte[])} method later. + * + * @param commandApdu The APDU that received from the remote device + * @param extras A bundle containing extra data. May be null. + * @return a byte-array containing the response APDU, or null if no response APDU can be sent + * at this point. + */ + // BEGIN_INCLUDE(processCommandApdu) + @Override + public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) { + Log.i(TAG, "Received APDU: " + ByteArrayToHexString(commandApdu)); + // If the APDU matches the SELECT AID command for this service, + // send the loyalty card account number, followed by a SELECT_OK status trailer (0x9000). + if (Arrays.equals(SELECT_APDU, commandApdu)) { + String account = AccountStorage.GetAccount(this); + byte[] accountBytes = account.getBytes(); + Log.i(TAG, "Sending account number: " + account); + return ConcatArrays(accountBytes, SELECT_OK_SW); + } else { + return UNKNOWN_CMD_SW; + } + } + // END_INCLUDE(processCommandApdu) + + /** + * Build APDU for SELECT AID command. This command indicates which service a reader is + * interested in communicating with. See ISO 7816-4. + * + * @param aid Application ID (AID) to select + * @return APDU for SELECT AID command + */ + public static byte[] BuildSelectApdu(String aid) { + // Format: [CLASS | INSTRUCTION | PARAMETER 1 | PARAMETER 2 | LENGTH | DATA] + return HexStringToByteArray(SELECT_APDU_HEADER + String.format("%02X", + aid.length() / 2) + aid); + } + + /** + * Utility method to convert a byte array to a hexadecimal string. + * + * @param bytes Bytes to convert + * @return String, containing hexadecimal representation. + */ + public static String ByteArrayToHexString(byte[] bytes) { + final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + char[] hexChars = new char[bytes.length * 2]; // Each byte has two hex characters (nibbles) + int v; + for (int j = 0; j < bytes.length; j++) { + v = bytes[j] & 0xFF; // Cast bytes[j] to int, treating as unsigned value + hexChars[j * 2] = hexArray[v >>> 4]; // Select hex character from upper nibble + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; // Select hex character from lower nibble + } + return new String(hexChars); + } + + /** + * Utility method to convert a hexadecimal string to a byte string. + * + *
Behavior with input strings containing non-hexadecimal characters is undefined. + * + * @param s String containing hexadecimal characters to convert + * @return Byte array generated from input + * @throws java.lang.IllegalArgumentException if input length is incorrect + */ + public static byte[] HexStringToByteArray(String s) throws IllegalArgumentException { + int len = s.length(); + if (len % 2 == 1) { + throw new IllegalArgumentException("Hex string must have even number of characters"); + } + byte[] data = new byte[len / 2]; // Allocate 1 byte per 2 hex characters + for (int i = 0; i < len; i += 2) { + // Convert each character into a integer (base-16), then bit-shift into place + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i+1), 16)); + } + return data; + } + + /** + * Utility method to concatenate two byte arrays. + * @param first First array + * @param rest Any remaining arrays + * @return Concatenated copy of input arrays + */ + public static byte[] ConcatArrays(byte[] first, byte[]... rest) { + int totalLength = first.length; + for (byte[] array : rest) { + totalLength += array.length; + } + byte[] result = Arrays.copyOf(first, totalLength); + int offset = first.length; + for (byte[] array : rest) { + System.arraycopy(array, 0, result, offset, array.length); + offset += array.length; + } + return result; + } +} diff --git a/samples/browseable/CardEmulation/src/com.example.android.cardemulation/MainActivity.java b/samples/browseable/CardEmulation/src/com.example.android.cardemulation/MainActivity.java new file mode 100644 index 000000000..0515609d0 --- /dev/null +++ b/samples/browseable/CardEmulation/src/com.example.android.cardemulation/MainActivity.java @@ -0,0 +1,110 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + + + + +package com.example.android.cardemulation; + +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.ViewAnimator; + +import com.example.android.common.activities.SampleActivityBase; +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogFragment; +import com.example.android.common.logger.LogWrapper; +import com.example.android.common.logger.MessageOnlyLogFilter; + +/** + * A simple launcher activity containing a summary sample description, sample log and a custom + * {@link android.support.v4.app.Fragment} which can display a view. + *
+ * For devices with displays with a width of 720dp or greater, the sample log is always visible, + * on other devices it's visibility is controlled by an item on the Action Bar. + */ +public class MainActivity extends SampleActivityBase { + + public static final String TAG = "MainActivity"; + + // Whether the Log Fragment is currently shown + private boolean mLogShown; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + CardEmulationFragment fragment = new CardEmulationFragment(); + transaction.replace(R.id.sample_content_fragment, fragment); + transaction.commit(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + MenuItem logToggle = menu.findItem(R.id.menu_toggle_log); + logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator); + logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log); + + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch(item.getItemId()) { + case R.id.menu_toggle_log: + mLogShown = !mLogShown; + ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output); + if (mLogShown) { + output.setDisplayedChild(1); + } else { + output.setDisplayedChild(0); + } + supportInvalidateOptionsMenu(); + return true; + } + return super.onOptionsItemSelected(item); + } + + /** Create a chain of targets that will receive log data */ + @Override + public void initializeLogging() { + // Wraps Android's native log framework. + LogWrapper logWrapper = new LogWrapper(); + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + Log.setLogNode(logWrapper); + + // Filter strips out everything except the message text. + MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); + logWrapper.setNext(msgFilter); + + // On screen logging via a fragment with a TextView. + LogFragment logFragment = (LogFragment) getSupportFragmentManager() + .findFragmentById(R.id.log_fragment); + msgFilter.setNext(logFragment.getLogView()); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/CardEmulation/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/CardEmulation/src/com.example.android.common/activities/SampleActivityBase.java new file mode 100644 index 000000000..3228927b7 --- /dev/null +++ b/samples/browseable/CardEmulation/src/com.example.android.common/activities/SampleActivityBase.java @@ -0,0 +1,52 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.example.android.common.activities; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogWrapper; + +/** + * Base launcher activity, to handle most of the common plumbing for samples. + */ +public class SampleActivityBase extends FragmentActivity { + + public static final String TAG = "SampleActivityBase"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + initializeLogging(); + } + + /** Set up targets to receive log data */ + public void initializeLogging() { + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/CardEmulation/src/com.example.android.common/logger/Log.java b/samples/browseable/CardEmulation/src/com.example.android.common/logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/CardEmulation/src/com.example.android.common/logger/Log.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + *
When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.
+ */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/CardEmulation/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/CardEmulation/src/com.example.android.common/logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/CardEmulation/src/com.example.android.common/logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +} \ No newline at end of file diff --git a/samples/browseable/CardEmulation/src/com.example.android.common/logger/LogNode.java b/samples/browseable/CardEmulation/src/com.example.android.common/logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/CardEmulation/src/com.example.android.common/logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/CardEmulation/src/com.example.android.common/logger/LogView.java b/samples/browseable/CardEmulation/src/com.example.android.common/logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/CardEmulation/src/com.example.android.common/logger/LogView.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/CardEmulation/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/CardEmulation/src/com.example.android.common/logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/CardEmulation/src/com.example.android.common/logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/CardEmulation/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/CardEmulation/src/com.example.android.common/logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/CardEmulation/src/com.example.android.common/logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/CardReader/AndroidManifest.xml b/samples/browseable/CardReader/AndroidManifest.xml new file mode 100644 index 000000000..a8ebd133a --- /dev/null +++ b/samples/browseable/CardReader/AndroidManifest.xml @@ -0,0 +1,53 @@ + + + ++ This sample demonstrates how to implement a low-level NFC card reader, for + reading cards that do not contain NDEF or Android Beam data. This sample is + designed to read the virtual loyalty card implemented in the CardEmulation sample. +
+ ++ In particular, this sample demonstrates how to disable Android Beam, select + which AIDs the reader is interested in, and establish communication with the + card. See Host-based + Card Emulation for more information on the HCE APIs. +
diff --git a/samples/browseable/CardReader/res/drawable-hdpi/ic_launcher.png b/samples/browseable/CardReader/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 000000000..9114e4428 Binary files /dev/null and b/samples/browseable/CardReader/res/drawable-hdpi/ic_launcher.png differ diff --git a/samples/browseable/CardReader/res/drawable-hdpi/tile.9.png b/samples/browseable/CardReader/res/drawable-hdpi/tile.9.png new file mode 100644 index 000000000..135862883 Binary files /dev/null and b/samples/browseable/CardReader/res/drawable-hdpi/tile.9.png differ diff --git a/samples/browseable/CardReader/res/drawable-mdpi/ic_launcher.png b/samples/browseable/CardReader/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 000000000..604fbd8d5 Binary files /dev/null and b/samples/browseable/CardReader/res/drawable-mdpi/ic_launcher.png differ diff --git a/samples/browseable/CardReader/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/CardReader/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 000000000..9e58d22db Binary files /dev/null and b/samples/browseable/CardReader/res/drawable-xhdpi/ic_launcher.png differ diff --git a/samples/browseable/CardReader/res/drawable-xxhdpi/card_background.png b/samples/browseable/CardReader/res/drawable-xxhdpi/card_background.png new file mode 100644 index 000000000..86a7ba764 Binary files /dev/null and b/samples/browseable/CardReader/res/drawable-xxhdpi/card_background.png differ diff --git a/samples/browseable/CardReader/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/CardReader/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..c550c0454 Binary files /dev/null and b/samples/browseable/CardReader/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/samples/browseable/CardReader/res/layout-w720dp/activity_main.xml b/samples/browseable/CardReader/res/layout-w720dp/activity_main.xml new file mode 100755 index 000000000..c9a52f621 --- /dev/null +++ b/samples/browseable/CardReader/res/layout-w720dp/activity_main.xml @@ -0,0 +1,73 @@ + +Communication with the card should take place here. + * + * @param tag Discovered tag + */ + @Override + public void onTagDiscovered(Tag tag) { + Log.i(TAG, "New tag discovered"); + // Android's Host-based Card Emulation (HCE) feature implements the ISO-DEP (ISO 14443-4) + // protocol. + // + // In order to communicate with a device using HCE, the discovered tag should be processed + // using the IsoDep class. + IsoDep isoDep = IsoDep.get(tag); + if (isoDep != null) { + try { + // Connect to the remote NFC device + isoDep.connect(); + // Build SELECT AID command for our loyalty card service. + // This command tells the remote device which service we wish to communicate with. + Log.i(TAG, "Requesting remote AID: " + SAMPLE_LOYALTY_CARD_AID); + byte[] command = BuildSelectApdu(SAMPLE_LOYALTY_CARD_AID); + // Send command to remote device + Log.i(TAG, "Sending: " + ByteArrayToHexString(command)); + byte[] result = isoDep.transceive(command); + // If AID is successfully selected, 0x9000 is returned as the status word (last 2 + // bytes of the result) by convention. Everything before the status word is + // optional payload, which is used here to hold the account number. + int resultLength = result.length; + byte[] statusWord = {result[resultLength-2], result[resultLength-1]}; + byte[] payload = Arrays.copyOf(result, resultLength-2); + if (Arrays.equals(SELECT_OK_SW, statusWord)) { + // The remote NFC device will immediately respond with its stored account number + String accountNumber = new String(payload, "UTF-8"); + Log.i(TAG, "Received: " + accountNumber); + // Inform CardReaderFragment of received account number + mAccountCallback.get().onAccountReceived(accountNumber); + } + } catch (IOException e) { + Log.e(TAG, "Error communicating with card: " + e.toString()); + } + } + } + + /** + * Build APDU for SELECT AID command. This command indicates which service a reader is + * interested in communicating with. See ISO 7816-4. + * + * @param aid Application ID (AID) to select + * @return APDU for SELECT AID command + */ + public static byte[] BuildSelectApdu(String aid) { + // Format: [CLASS | INSTRUCTION | PARAMETER 1 | PARAMETER 2 | LENGTH | DATA] + return HexStringToByteArray(SELECT_APDU_HEADER + String.format("%02X", aid.length() / 2) + aid); + } + + /** + * Utility class to convert a byte array to a hexadecimal string. + * + * @param bytes Bytes to convert + * @return String, containing hexadecimal representation. + */ + public static String ByteArrayToHexString(byte[] bytes) { + final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + char[] hexChars = new char[bytes.length * 2]; + int v; + for ( int j = 0; j < bytes.length; j++ ) { + v = bytes[j] & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + } + return new String(hexChars); + } + + /** + * Utility class to convert a hexadecimal string to a byte string. + * + *
Behavior with input strings containing non-hexadecimal characters is undefined. + * + * @param s String containing hexadecimal characters to convert + * @return Byte array generated from input + */ + public static byte[] HexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i+1), 16)); + } + return data; + } + +} diff --git a/samples/browseable/CardReader/src/com.example.android.cardreader/MainActivity.java b/samples/browseable/CardReader/src/com.example.android.cardreader/MainActivity.java new file mode 100644 index 000000000..e0280e998 --- /dev/null +++ b/samples/browseable/CardReader/src/com.example.android.cardreader/MainActivity.java @@ -0,0 +1,110 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + + + + +package com.example.android.cardreader; + +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.ViewAnimator; + +import com.example.android.common.activities.SampleActivityBase; +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogFragment; +import com.example.android.common.logger.LogWrapper; +import com.example.android.common.logger.MessageOnlyLogFilter; + +/** + * A simple launcher activity containing a summary sample description, sample log and a custom + * {@link android.support.v4.app.Fragment} which can display a view. + *
+ * For devices with displays with a width of 720dp or greater, the sample log is always visible, + * on other devices it's visibility is controlled by an item on the Action Bar. + */ +public class MainActivity extends SampleActivityBase { + + public static final String TAG = "MainActivity"; + + // Whether the Log Fragment is currently shown + private boolean mLogShown; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + CardReaderFragment fragment = new CardReaderFragment(); + transaction.replace(R.id.sample_content_fragment, fragment); + transaction.commit(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + MenuItem logToggle = menu.findItem(R.id.menu_toggle_log); + logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator); + logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log); + + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch(item.getItemId()) { + case R.id.menu_toggle_log: + mLogShown = !mLogShown; + ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output); + if (mLogShown) { + output.setDisplayedChild(1); + } else { + output.setDisplayedChild(0); + } + supportInvalidateOptionsMenu(); + return true; + } + return super.onOptionsItemSelected(item); + } + + /** Create a chain of targets that will receive log data */ + @Override + public void initializeLogging() { + // Wraps Android's native log framework. + LogWrapper logWrapper = new LogWrapper(); + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + Log.setLogNode(logWrapper); + + // Filter strips out everything except the message text. + MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); + logWrapper.setNext(msgFilter); + + // On screen logging via a fragment with a TextView. + LogFragment logFragment = (LogFragment) getSupportFragmentManager() + .findFragmentById(R.id.log_fragment); + msgFilter.setNext(logFragment.getLogView()); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/CardReader/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/CardReader/src/com.example.android.common/activities/SampleActivityBase.java new file mode 100644 index 000000000..3228927b7 --- /dev/null +++ b/samples/browseable/CardReader/src/com.example.android.common/activities/SampleActivityBase.java @@ -0,0 +1,52 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.example.android.common.activities; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogWrapper; + +/** + * Base launcher activity, to handle most of the common plumbing for samples. + */ +public class SampleActivityBase extends FragmentActivity { + + public static final String TAG = "SampleActivityBase"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + initializeLogging(); + } + + /** Set up targets to receive log data */ + public void initializeLogging() { + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/CardReader/src/com.example.android.common/logger/Log.java b/samples/browseable/CardReader/src/com.example.android.common/logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/CardReader/src/com.example.android.common/logger/Log.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + *
When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.
+ */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/CardReader/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/CardReader/src/com.example.android.common/logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/CardReader/src/com.example.android.common/logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +} \ No newline at end of file diff --git a/samples/browseable/CardReader/src/com.example.android.common/logger/LogNode.java b/samples/browseable/CardReader/src/com.example.android.common/logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/CardReader/src/com.example.android.common/logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/CardReader/src/com.example.android.common/logger/LogView.java b/samples/browseable/CardReader/src/com.example.android.common/logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/CardReader/src/com.example.android.common/logger/LogView.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/CardReader/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/CardReader/src/com.example.android.common/logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/CardReader/src/com.example.android.common/logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/CardReader/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/CardReader/src/com.example.android.common/logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/CardReader/src/com.example.android.common/logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/Styled/res/values-sw600dp/dimens.xml b/samples/browseable/CustomChoiceList/res/values-sw600dp/template-dimens.xml similarity index 100% rename from samples/browseable/Styled/res/values-sw600dp/dimens.xml rename to samples/browseable/CustomChoiceList/res/values-sw600dp/template-dimens.xml diff --git a/samples/browseable/Styled/res/values-sw600dp/styles.xml b/samples/browseable/CustomChoiceList/res/values-sw600dp/template-styles.xml similarity index 100% rename from samples/browseable/Styled/res/values-sw600dp/styles.xml rename to samples/browseable/CustomChoiceList/res/values-sw600dp/template-styles.xml diff --git a/samples/browseable/CustomChoiceList/res/values/dimens.xml b/samples/browseable/CustomChoiceList/res/values/dimens.xml index 39e710b5c..c22027efa 100644 --- a/samples/browseable/CustomChoiceList/res/values/dimens.xml +++ b/samples/browseable/CustomChoiceList/res/values/dimens.xml @@ -16,17 +16,8 @@This is a sample application for the +Displaying +Bitmaps Efficiently Android Training class.
+ ++The sample demonstrates: +
+When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.
+ */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogFragment.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +} \ No newline at end of file diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogNode.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogView.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogView.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogWrapper.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/MessageOnlyLogFilter.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.common.logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/provider/Images.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/provider/Images.java similarity index 99% rename from samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/provider/Images.java rename to samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/provider/Images.java index fcf4496bd..5a895462d 100644 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/provider/Images.java +++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/provider/Images.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.example.android.bitmapfun.provider; +package com.example.android.displayingbitmaps.provider; /** * Some simple test data to use for this sample app. diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailActivity.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageDetailActivity.java similarity index 94% rename from samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailActivity.java rename to samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageDetailActivity.java index 05d7b03f0..c2be1acfc 100644 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailActivity.java +++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageDetailActivity.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.example.android.bitmapfun.ui; +package com.example.android.displayingbitmaps.ui; import android.annotation.TargetApi; import android.app.ActionBar; @@ -34,12 +34,12 @@ import android.view.View.OnClickListener; import android.view.WindowManager.LayoutParams; import android.widget.Toast; -import com.example.android.bitmapfun.BuildConfig; -import com.example.android.bitmapfun.R; -import com.example.android.bitmapfun.provider.Images; -import com.example.android.bitmapfun.util.ImageCache; -import com.example.android.bitmapfun.util.ImageFetcher; -import com.example.android.bitmapfun.util.Utils; +import com.example.android.displayingbitmaps.BuildConfig; +import com.example.android.displayingbitmaps.R; +import com.example.android.displayingbitmaps.provider.Images; +import com.example.android.displayingbitmaps.util.ImageCache; +import com.example.android.displayingbitmaps.util.ImageFetcher; +import com.example.android.displayingbitmaps.util.Utils; public class ImageDetailActivity extends FragmentActivity implements OnClickListener { private static final String IMAGE_CACHE_DIR = "images"; @@ -85,7 +85,7 @@ public class ImageDetailActivity extends FragmentActivity implements OnClickList mAdapter = new ImagePagerAdapter(getSupportFragmentManager(), Images.imageUrls.length); mPager = (ViewPager) findViewById(R.id.pager); mPager.setAdapter(mAdapter); - mPager.setPageMargin((int) getResources().getDimension(R.dimen.image_detail_pager_margin)); + mPager.setPageMargin((int) getResources().getDimension(R.dimen.horizontal_page_margin)); mPager.setOffscreenPageLimit(2); // Set up activity to go full screen diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailFragment.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageDetailFragment.java similarity index 92% rename from samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailFragment.java rename to samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageDetailFragment.java index 9fff8a08a..506729a7e 100644 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailFragment.java +++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageDetailFragment.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.example.android.bitmapfun.ui; +package com.example.android.displayingbitmaps.ui; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -24,10 +24,10 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ImageView; -import com.example.android.bitmapfun.R; -import com.example.android.bitmapfun.util.ImageFetcher; -import com.example.android.bitmapfun.util.ImageWorker; -import com.example.android.bitmapfun.util.Utils; +import com.example.android.displayingbitmaps.R; +import com.example.android.displayingbitmaps.util.ImageFetcher; +import com.example.android.displayingbitmaps.util.ImageWorker; +import com.example.android.displayingbitmaps.util.Utils; /** * This fragment will populate the children of the ViewPager from {@link ImageDetailActivity}. diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridActivity.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageGridActivity.java similarity index 89% rename from samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridActivity.java rename to samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageGridActivity.java index e7c7d1c43..f171955ae 100644 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridActivity.java +++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageGridActivity.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.example.android.bitmapfun.ui; +package com.example.android.displayingbitmaps.ui; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentTransaction; -import com.example.android.bitmapfun.BuildConfig; -import com.example.android.bitmapfun.util.Utils; +import com.example.android.displayingbitmaps.BuildConfig; +import com.example.android.displayingbitmaps.util.Utils; /** * Simple FragmentActivity to hold the main {@link ImageGridFragment} and not much else. diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridFragment.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageGridFragment.java similarity index 94% rename from samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridFragment.java rename to samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageGridFragment.java index ba4581aa1..4eb1f1b35 100644 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridFragment.java +++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/ImageGridFragment.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.example.android.bitmapfun.ui; +package com.example.android.displayingbitmaps.ui; import android.annotation.TargetApi; import android.app.ActivityOptions; @@ -23,7 +23,6 @@ import android.content.Intent; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.support.v4.app.Fragment; -import android.util.Log; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.Menu; @@ -40,12 +39,13 @@ import android.widget.GridView; import android.widget.ImageView; import android.widget.Toast; -import com.example.android.bitmapfun.BuildConfig; -import com.example.android.bitmapfun.R; -import com.example.android.bitmapfun.provider.Images; -import com.example.android.bitmapfun.util.ImageCache.ImageCacheParams; -import com.example.android.bitmapfun.util.ImageFetcher; -import com.example.android.bitmapfun.util.Utils; +import com.example.android.common.logger.Log; +import com.example.android.displayingbitmaps.BuildConfig; +import com.example.android.displayingbitmaps.R; +import com.example.android.displayingbitmaps.provider.Images; +import com.example.android.displayingbitmaps.util.ImageCache; +import com.example.android.displayingbitmaps.util.ImageFetcher; +import com.example.android.displayingbitmaps.util.Utils; /** * The main fragment that powers the ImageGridActivity screen. Fairly straight forward GridView @@ -78,7 +78,8 @@ public class ImageGridFragment extends Fragment implements AdapterView.OnItemCli mAdapter = new ImageAdapter(getActivity()); - ImageCacheParams cacheParams = new ImageCacheParams(getActivity(), IMAGE_CACHE_DIR); + ImageCache.ImageCacheParams cacheParams = + new ImageCache.ImageCacheParams(getActivity(), IMAGE_CACHE_DIR); cacheParams.setMemCacheSizePercent(0.25f); // Set memory cache to 25% of app memory @@ -122,6 +123,7 @@ public class ImageGridFragment extends Fragment implements AdapterView.OnItemCli // of each view so we get nice square thumbnails. mGridView.getViewTreeObserver().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { + @TargetApi(VERSION_CODES.JELLY_BEAN) @Override public void onGlobalLayout() { if (mAdapter.getNumColumns() == 0) { @@ -272,6 +274,7 @@ public class ImageGridFragment extends Fragment implements AdapterView.OnItemCli @Override public View getView(int position, View convertView, ViewGroup container) { + //BEGIN_INCLUDE(load_gridview_item) // First check if this is the top row if (position < mNumColumns) { if (convertView == null) { @@ -279,7 +282,7 @@ public class ImageGridFragment extends Fragment implements AdapterView.OnItemCli } // Set empty view with height of ActionBar convertView.setLayoutParams(new AbsListView.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, mActionBarHeight)); + LayoutParams.MATCH_PARENT, mActionBarHeight)); return convertView; } @@ -302,6 +305,7 @@ public class ImageGridFragment extends Fragment implements AdapterView.OnItemCli // setting a placeholder image while the background thread runs mImageFetcher.loadImage(Images.imageThumbUrls[position - mNumColumns], imageView); return imageView; + //END_INCLUDE(load_gridview_item) } /** diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/RecyclingImageView.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/RecyclingImageView.java similarity index 95% rename from samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/RecyclingImageView.java rename to samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/RecyclingImageView.java index 1bcc01465..1db134c31 100644 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/RecyclingImageView.java +++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/ui/RecyclingImageView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.example.android.bitmapfun.ui; +package com.example.android.displayingbitmaps.ui; import android.content.Context; import android.graphics.drawable.Drawable; @@ -22,7 +22,7 @@ import android.graphics.drawable.LayerDrawable; import android.util.AttributeSet; import android.widget.ImageView; -import com.example.android.bitmapfun.util.RecyclingBitmapDrawable; +import com.example.android.displayingbitmaps.util.RecyclingBitmapDrawable; /** * Sub-class of ImageView which automatically notifies the drawable when it is diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/AsyncTask.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/AsyncTask.java similarity index 96% rename from samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/AsyncTask.java rename to samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/AsyncTask.java index 018ce1a23..dffade48e 100644 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/AsyncTask.java +++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/AsyncTask.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.example.android.bitmapfun.util; +package com.example.android.displayingbitmaps.util; import android.annotation.TargetApi; import android.os.Handler; @@ -25,8 +25,8 @@ import java.util.ArrayDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; -import java.util.concurrent.Executor; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; import java.util.concurrent.LinkedBlockingQueue; @@ -56,12 +56,12 @@ import java.util.concurrent.atomic.AtomicInteger; * perform background operations and publish results on the UI thread without * having to manipulate threads and/or handlers. * - *AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler} + *
AsyncTask is designed to be a helper class around {@link Thread} and {@link android.os.Handler}
* and does not constitute a generic threading framework. AsyncTasks should ideally be
* used for short operations (a few seconds at the most.) If you need to keep threads
* running for long periods of time, it is highly recommended you use the various APIs
- * provided by the java.util.concurrent pacakge such as {@link Executor},
- * {@link ThreadPoolExecutor} and {@link FutureTask}.
java.util.concurrent pacakge such as {@link java.util.concurrent.Executor},
+ * {@link java.util.concurrent.ThreadPoolExecutor} and {@link java.util.concurrent.FutureTask}.
*
* An asynchronous task is defined by a computation that runs on a background thread and
* whose result is published on the UI thread. An asynchronous task is defined by 3 generic
@@ -213,7 +213,7 @@ public abstract class AsyncTask This method is typically used with {@link #THREAD_POOL_EXECUTOR} to
* allow multiple tasks to run in parallel on a pool of threads managed by
- * AsyncTask, however you can also use your own {@link Executor} for custom
+ * AsyncTask, however you can also use your own {@link java.util.concurrent.Executor} for custom
* behavior.
*
* Warning: Allowing multiple tasks to run in parallel from
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/DiskLruCache.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/DiskLruCache.java
similarity index 99%
rename from samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/DiskLruCache.java
rename to samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/DiskLruCache.java
index 26cdbd781..4c14345ae 100644
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/DiskLruCache.java
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/DiskLruCache.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.bitmapfun.util;
+package com.example.android.displayingbitmaps.util;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
@@ -302,7 +302,7 @@ public final class DiskLruCache implements Closeable {
* @param appVersion
* @param valueCount the number of values per cache entry. Must be positive.
* @param maxSize the maximum number of bytes this cache should use to store
- * @throws IOException if reading or writing the cache directory fails
+ * @throws java.io.IOException if reading or writing the cache directory fails
*/
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
throws IOException {
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageCache.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageCache.java
similarity index 95%
rename from samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageCache.java
rename to samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageCache.java
index 7021d2c89..41da56c0b 100644
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageCache.java
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageCache.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.bitmapfun.util;
+package com.example.android.displayingbitmaps.util;
import android.annotation.TargetApi;
import android.content.Context;
@@ -30,9 +30,9 @@ import android.os.StatFs;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.util.LruCache;
-import android.util.Log;
-import com.example.android.bitmapfun.BuildConfig;
+import com.example.android.common.logger.Log;
+import com.example.android.displayingbitmaps.BuildConfig;
import java.io.File;
import java.io.FileDescriptor;
@@ -51,9 +51,9 @@ import java.util.Set;
/**
* This class handles disk and memory caching of bitmaps in conjunction with the
* {@link ImageWorker} class and its subclasses. Use
- * {@link ImageCache#getInstance(FragmentManager, ImageCacheParams)} to get an instance of this
+ * {@link ImageCache#getInstance(android.support.v4.app.FragmentManager, ImageCacheParams)} to get an instance of this
* class, although usually a cache should be added directly to an {@link ImageWorker} by calling
- * {@link ImageWorker#addImageCache(FragmentManager, ImageCacheParams)}.
+ * {@link ImageWorker#addImageCache(android.support.v4.app.FragmentManager, ImageCacheParams)}.
*/
public class ImageCache {
private static final String TAG = "ImageCache";
@@ -85,7 +85,7 @@ public class ImageCache {
/**
* Create a new ImageCache object using the specified parameters. This should not be
* called directly by other classes, instead use
- * {@link ImageCache#getInstance(FragmentManager, ImageCacheParams)} to fetch an ImageCache
+ * {@link ImageCache#getInstance(android.support.v4.app.FragmentManager, ImageCacheParams)} to fetch an ImageCache
* instance.
*
* @param cacheParams The cache parameters to use to initialize the cache
@@ -128,6 +128,7 @@ public class ImageCache {
private void init(ImageCacheParams cacheParams) {
mCacheParams = cacheParams;
+ //BEGIN_INCLUDE(init_memory_cache)
// Set up memory cache
if (mCacheParams.memoryCacheEnabled) {
if (BuildConfig.DEBUG) {
@@ -157,7 +158,7 @@ public class ImageCache {
protected void entryRemoved(boolean evicted, String key,
BitmapDrawable oldValue, BitmapDrawable newValue) {
if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
- // The removed entry is a recycling drawable, so notify it
+ // The removed entry is a recycling drawable, so notify it
// that it has been removed from the memory cache
((RecyclingBitmapDrawable) oldValue).setIsCached(false);
} else {
@@ -182,6 +183,7 @@ public class ImageCache {
}
};
}
+ //END_INCLUDE(init_memory_cache)
// By default the disk cache is not initialized here as it should be initialized
// on a separate thread due to disk access.
@@ -231,6 +233,7 @@ public class ImageCache {
* @param value The bitmap drawable to store
*/
public void addBitmapToCache(String data, BitmapDrawable value) {
+ //BEGIN_INCLUDE(add_bitmap_to_cache)
if (data == null || value == null) {
return;
}
@@ -238,7 +241,7 @@ public class ImageCache {
// Add to memory cache
if (mMemoryCache != null) {
if (RecyclingBitmapDrawable.class.isInstance(value)) {
- // The removed entry is a recycling drawable, so notify it
+ // The removed entry is a recycling drawable, so notify it
// that it has been added into the memory cache
((RecyclingBitmapDrawable) value).setIsCached(true);
}
@@ -277,6 +280,7 @@ public class ImageCache {
}
}
}
+ //END_INCLUDE(add_bitmap_to_cache)
}
/**
@@ -286,6 +290,7 @@ public class ImageCache {
* @return The bitmap drawable if found in cache, null otherwise
*/
public BitmapDrawable getBitmapFromMemCache(String data) {
+ //BEGIN_INCLUDE(get_bitmap_from_mem_cache)
BitmapDrawable memValue = null;
if (mMemoryCache != null) {
@@ -297,6 +302,7 @@ public class ImageCache {
}
return memValue;
+ //END_INCLUDE(get_bitmap_from_mem_cache)
}
/**
@@ -306,6 +312,7 @@ public class ImageCache {
* @return The bitmap if found in cache, null otherwise
*/
public Bitmap getBitmapFromDiskCache(String data) {
+ //BEGIN_INCLUDE(get_bitmap_from_disk_cache)
final String key = hashKeyForDisk(data);
Bitmap bitmap = null;
@@ -345,6 +352,7 @@ public class ImageCache {
}
return bitmap;
}
+ //END_INCLUDE(get_bitmap_from_disk_cache)
}
/**
@@ -352,6 +360,7 @@ public class ImageCache {
* @return Bitmap that case be used for inBitmap
*/
protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {
+ //BEGIN_INCLUDE(get_bitmap_from_reusable_set)
Bitmap bitmap = null;
if (mReusableBitmaps != null && !mReusableBitmaps.isEmpty()) {
@@ -380,6 +389,7 @@ public class ImageCache {
}
return bitmap;
+ //END_INCLUDE(get_bitmap_from_reusable_set)
}
/**
@@ -467,8 +477,8 @@ public class ImageCache {
/**
* Create a set of image cache parameters that can be provided to
- * {@link ImageCache#getInstance(FragmentManager, ImageCacheParams)} or
- * {@link ImageWorker#addImageCache(FragmentManager, ImageCacheParams)}.
+ * {@link ImageCache#getInstance(android.support.v4.app.FragmentManager, ImageCacheParams)} or
+ * {@link ImageWorker#addImageCache(android.support.v4.app.FragmentManager, ImageCacheParams)}.
* @param context A context to use.
* @param diskCacheDirectoryName A unique subdirectory name that will be appended to the
* application cache directory. Usually "cache" or "images"
@@ -509,7 +519,7 @@ public class ImageCache {
@TargetApi(VERSION_CODES.KITKAT)
private static boolean canUseForInBitmap(
Bitmap candidate, BitmapFactory.Options targetOptions) {
-
+ //BEGIN_INCLUDE(can_use_for_inbitmap)
if (!Utils.hasKitKat()) {
// On earlier versions, the dimensions must match exactly and the inSampleSize must be 1
return candidate.getWidth() == targetOptions.outWidth
@@ -523,6 +533,7 @@ public class ImageCache {
int height = targetOptions.outHeight / targetOptions.inSampleSize;
int byteCount = width * height * getBytesPerPixel(candidate.getConfig());
return byteCount <= candidate.getAllocationByteCount();
+ //END_INCLUDE(can_use_for_inbitmap)
}
/**
@@ -671,6 +682,7 @@ public class ImageCache {
* created.
*/
private static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
+ //BEGIN_INCLUDE(find_create_retain_fragment)
// Check to see if we have retained the worker fragment.
RetainFragment mRetainFragment = (RetainFragment) fm.findFragmentByTag(TAG);
@@ -681,6 +693,7 @@ public class ImageCache {
}
return mRetainFragment;
+ //END_INCLUDE(find_create_retain_fragment)
}
/**
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageFetcher.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageFetcher.java
similarity index 98%
rename from samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageFetcher.java
rename to samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageFetcher.java
index 4c92d7417..b9ccb68cf 100644
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageFetcher.java
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageFetcher.java
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-package com.example.android.bitmapfun.util;
+package com.example.android.displayingbitmaps.util;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
-import android.util.Log;
import android.widget.Toast;
-import com.example.android.bitmapfun.BuildConfig;
-import com.example.android.bitmapfun.R;
+import com.example.android.common.logger.Log;
+import com.example.android.displayingbitmaps.BuildConfig;
+import com.example.android.displayingbitmaps.R;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageResizer.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageResizer.java
similarity index 93%
rename from samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageResizer.java
rename to samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageResizer.java
index e8ce4e12e..be9109682 100644
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageResizer.java
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageResizer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.bitmapfun.util;
+package com.example.android.displayingbitmaps.util;
import android.annotation.TargetApi;
import android.content.Context;
@@ -22,9 +22,9 @@ import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
-import android.util.Log;
-import com.example.android.bitmapfun.BuildConfig;
+import com.example.android.common.logger.Log;
+import com.example.android.displayingbitmaps.BuildConfig;
import java.io.FileDescriptor;
@@ -115,6 +115,7 @@ public class ImageResizer extends ImageWorker {
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight, ImageCache cache) {
+ // BEGIN_INCLUDE (read_bitmap_dimensions)
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
@@ -122,6 +123,7 @@ public class ImageResizer extends ImageWorker {
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
+ // END_INCLUDE (read_bitmap_dimensions)
// If we're running on Honeycomb or newer, try to use inBitmap
if (Utils.hasHoneycomb()) {
@@ -198,6 +200,7 @@ public class ImageResizer extends ImageWorker {
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private static void addInBitmapOptions(BitmapFactory.Options options, ImageCache cache) {
+ //BEGIN_INCLUDE(add_bitmap_options)
// inBitmap only works with mutable bitmaps so force the decoder to
// return mutable bitmaps.
options.inMutable = true;
@@ -210,11 +213,12 @@ public class ImageResizer extends ImageWorker {
options.inBitmap = inBitmap;
}
}
+ //END_INCLUDE(add_bitmap_options)
}
/**
- * Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
- * bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
+ * Calculate an inSampleSize for use in a {@link android.graphics.BitmapFactory.Options} object when decoding
+ * bitmaps using the decode* methods from {@link android.graphics.BitmapFactory}. This implementation calculates
* the closest inSampleSize that is a power of 2 and will result in the final decoded bitmap
* having a width and height equal to or larger than the requested width and height.
*
@@ -226,6 +230,7 @@ public class ImageResizer extends ImageWorker {
*/
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
+ // BEGIN_INCLUDE (calculate_sample_size)
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
@@ -260,5 +265,6 @@ public class ImageResizer extends ImageWorker {
}
}
return inSampleSize;
+ // END_INCLUDE (calculate_sample_size)
}
}
diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageWorker.java b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageWorker.java
similarity index 92%
rename from samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageWorker.java
rename to samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageWorker.java
index d26066011..f44d00d5b 100644
--- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageWorker.java
+++ b/samples/browseable/DisplayingBitmaps/src/com.example.android.displayingbitmaps/util/ImageWorker.java
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-package com.example.android.bitmapfun.util;
-
-import com.example.android.bitmapfun.BuildConfig;
+package com.example.android.displayingbitmaps.util;
import android.content.Context;
import android.content.res.Resources;
@@ -28,9 +26,11 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
-import android.util.Log;
import android.widget.ImageView;
+import com.example.android.common.logger.Log;
+import com.example.android.displayingbitmaps.BuildConfig;
+
import java.lang.ref.WeakReference;
/**
@@ -65,7 +65,7 @@ public abstract class ImageWorker {
* Load an image specified by the data parameter into an ImageView (override
* {@link ImageWorker#processBitmap(Object)} to define the processing logic). A memory and
* disk cache will be used if an {@link ImageCache} has been added using
- * {@link ImageWorker#addImageCache(FragmentManager, ImageCache.ImageCacheParams)}. If the
+ * {@link ImageWorker#addImageCache(android.support.v4.app.FragmentManager, ImageCache.ImageCacheParams)}. If the
* image is found in the memory cache, it is set immediately, otherwise an {@link AsyncTask}
* will be created to asynchronously load the bitmap.
*
@@ -87,7 +87,8 @@ public abstract class ImageWorker {
// Bitmap found in memory cache
imageView.setImageDrawable(value);
} else if (cancelPotentialWork(data, imageView)) {
- final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+ //BEGIN_INCLUDE(execute_background_task)
+ final BitmapWorkerTask task = new BitmapWorkerTask(data, imageView);
final AsyncDrawable asyncDrawable =
new AsyncDrawable(mResources, mLoadingBitmap, task);
imageView.setImageDrawable(asyncDrawable);
@@ -95,7 +96,8 @@ public abstract class ImageWorker {
// NOTE: This uses a custom version of AsyncTask that has been pulled from the
// framework and slightly modified. Refer to the docs at the top of the class
// for more info on what was changed.
- task.executeOnExecutor(AsyncTask.DUAL_THREAD_EXECUTOR, data);
+ task.executeOnExecutor(AsyncTask.DUAL_THREAD_EXECUTOR);
+ //END_INCLUDE(execute_background_task)
}
}
@@ -135,7 +137,7 @@ public abstract class ImageWorker {
* caching.
* @param activity
* @param diskCacheDirectoryName See
- * {@link ImageCache.ImageCacheParams#ImageCacheParams(Context, String)}.
+ * {@link ImageCache.ImageCacheParams#ImageCacheParams(android.content.Context, String)}.
*/
public void addImageCache(FragmentActivity activity, String diskCacheDirectoryName) {
mImageCacheParams = new ImageCache.ImageCacheParams(activity, diskCacheDirectoryName);
@@ -161,7 +163,7 @@ public abstract class ImageWorker {
* example, you could resize a large bitmap here, or pull down an image from the network.
*
* @param data The data to identify which image to process, as provided by
- * {@link ImageWorker#loadImage(Object, ImageView)}
+ * {@link ImageWorker#loadImage(Object, android.widget.ImageView)}
* @return The processed bitmap
*/
protected abstract Bitmap processBitmap(Object data);
@@ -182,7 +184,7 @@ public abstract class ImageWorker {
if (bitmapWorkerTask != null) {
bitmapWorkerTask.cancel(true);
if (BuildConfig.DEBUG) {
- final Object bitmapData = bitmapWorkerTask.data;
+ final Object bitmapData = bitmapWorkerTask.mData;
Log.d(TAG, "cancelWork - cancelled work for " + bitmapData);
}
}
@@ -195,10 +197,11 @@ public abstract class ImageWorker {
* stopped in that case.
*/
public static boolean cancelPotentialWork(Object data, ImageView imageView) {
+ //BEGIN_INCLUDE(cancel_potential_work)
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
- final Object bitmapData = bitmapWorkerTask.data;
+ final Object bitmapData = bitmapWorkerTask.mData;
if (bitmapData == null || !bitmapData.equals(data)) {
bitmapWorkerTask.cancel(true);
if (BuildConfig.DEBUG) {
@@ -210,6 +213,7 @@ public abstract class ImageWorker {
}
}
return true;
+ //END_INCLUDE(cancel_potential_work)
}
/**
@@ -231,11 +235,12 @@ public abstract class ImageWorker {
/**
* The actual AsyncTask that will asynchronously process the image.
*/
- private class BitmapWorkerTask extends AsyncTask