Update sdk.atree and samples/browseable for lastest samples release

Synced to developers/samples/android commit
97b2cfe5ba6d8fa8daaf3273141b321b5fe9e910.

Change-Id: I360cfa147e71dd519b841df41b4e878f86b9b27b
This commit is contained in:
Trevor Johns
2015-03-30 16:06:43 -07:00
parent d9e9e60f22
commit 77b5d394ff
657 changed files with 11219 additions and 6521 deletions

View File

@@ -280,9 +280,7 @@ developers/build/prebuilts/gradle/CardReader sam
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/CustomTransition samples/${PLATFORM_NAME}/ui/CustomTransition
developers/build/prebuilts/gradle/FragmentTransition samples/${PLATFORM_NAME}/ui/FragmentTransition
developers/build/prebuilts/gradle/SwipeRefreshLayoutBasic samples/${PLATFORM_NAME}/ui/SwipeRefreshLayoutBasic
developers/build/prebuilts/gradle/SwipeRefreshListFragment samples/${PLATFORM_NAME}/ui/SwipeRefreshListFragment
developers/build/prebuilts/gradle/SwipeRefreshMultipleViews samples/${PLATFORM_NAME}/ui/SwipeRefreshMultipleViews
@@ -314,6 +312,8 @@ developers/build/prebuilts/gradle/PermissionRequest sam
developers/build/prebuilts/gradle/DirectorySelection samples/${PLATFORM_NAME}/content/documentsUi/DirectorySelection
developers/build/prebuilts/gradle/AppUsageStatistics samples/${PLATFORM_NAME}/system/AppUsageStatistics
developers/build/prebuilts/gradle/ScreenCapture samples/${PLATFORM_NAME}/media/ScreenCapture
developers/build/prebuilts/gradle/NfcProvisioning samples/${PLATFORM_NAME}/nfc/NfcProvisioning
developers/build/prebuilts/gradle/DeviceOwner samples/${PLATFORM_NAME}/admin/DeviceOwner
developers/build/prebuilts/androidtv samples/${PLATFORM_NAME}/androidtv
@@ -322,7 +322,6 @@ developers/build/prebuilts/gradle/AgendaData samples/${PLATFO
developers/build/prebuilts/gradle/DataLayer samples/${PLATFORM_NAME}/wearable/DataLayer
developers/build/prebuilts/gradle/DelayedConfirmation samples/${PLATFORM_NAME}/wearable/DelayedConfirmation
developers/build/prebuilts/gradle/ElizaChat samples/${PLATFORM_NAME}/wearable/ElizaChat
developers/build/prebuilts/gradle/EmbeddedApp samples/${PLATFORM_NAME}/wearable/EmbeddedApp
developers/build/prebuilts/gradle/FindMyPhone samples/${PLATFORM_NAME}/wearable/FindMyPhone
developers/build/prebuilts/gradle/Flashlight samples/${PLATFORM_NAME}/wearable/Flashlight
developers/build/prebuilts/gradle/Geofencing samples/${PLATFORM_NAME}/wearable/Geofencing
@@ -337,6 +336,7 @@ developers/build/prebuilts/gradle/SynchronizedNotifications samples/${PLATFO
developers/build/prebuilts/gradle/Timer samples/${PLATFORM_NAME}/wearable/Timer
developers/build/prebuilts/gradle/WatchFace samples/${PLATFORM_NAME}/wearable/WatchFace
developers/build/prebuilts/gradle/WatchViewStub samples/${PLATFORM_NAME}/wearable/WatchViewStub
developers/build/prebuilts/gradle/XYZTouristAttractions samples/${PLATFORM_NAME}/wearable/XYZTouristAttractions
# Old sample tree
development/samples/AccelerometerPlay samples/${PLATFORM_NAME}/legacy/AccelerometerPlay
@@ -498,4 +498,3 @@ ${OUT_DIR}/target/common/obj/PACKAGING/android-support-v17-leanback_intermediate
##############################################################################
framework/layoutlib-tests.jar tests/libtests/layoutlib-tests.jar
system/app/EmulatorSmokeTests/EmulatorSmokeTests.apk tests/emulator-test-apps/EmulatorSmokeTests.apk

View File

@@ -1,36 +0,0 @@
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout style="@style/Widget.SampleMessageTile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView style="@style/Widget.SampleMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/horizontal_page_margin"
android:layout_marginRight="@dimen/horizontal_page_margin"
android:layout_marginTop="@dimen/vertical_page_margin"
android:layout_marginBottom="@dimen/vertical_page_margin"
android:text="@string/intro_message" />
</LinearLayout>
</LinearLayout>

View File

@@ -1,36 +0,0 @@
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout style="@style/Widget.SampleMessageTile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView style="@style/Widget.SampleMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/horizontal_page_margin"
android:layout_marginRight="@dimen/horizontal_page_margin"
android:layout_marginTop="@dimen/vertical_page_margin"
android:layout_marginBottom="@dimen/vertical_page_margin"
android:text="@string/intro_message" />
</LinearLayout>
</LinearLayout>

View File

@@ -1,36 +0,0 @@
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout style="@style/Widget.SampleMessageTile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView style="@style/Widget.SampleMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/horizontal_page_margin"
android:layout_marginRight="@dimen/horizontal_page_margin"
android:layout_marginTop="@dimen/vertical_page_margin"
android:layout_marginBottom="@dimen/vertical_page_margin"
android:text="@string/intro_message" />
</LinearLayout>
</LinearLayout>

View File

@@ -1,36 +0,0 @@
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout style="@style/Widget.SampleMessageTile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView style="@style/Widget.SampleMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/horizontal_page_margin"
android:layout_marginRight="@dimen/horizontal_page_margin"
android:layout_marginTop="@dimen/vertical_page_margin"
android:layout_marginBottom="@dimen/vertical_page_margin"
android:text="@string/intro_message" />
</LinearLayout>
</LinearLayout>

View File

@@ -1,36 +0,0 @@
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout style="@style/Widget.SampleMessageTile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView style="@style/Widget.SampleMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/horizontal_page_margin"
android:layout_marginRight="@dimen/horizontal_page_margin"
android:layout_marginTop="@dimen/vertical_page_margin"
android:layout_marginBottom="@dimen/vertical_page_margin"
android:text="@string/intro_message" />
</LinearLayout>
</LinearLayout>

View File

@@ -1,36 +0,0 @@
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout style="@style/Widget.SampleMessageTile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView style="@style/Widget.SampleMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/horizontal_page_margin"
android:layout_marginRight="@dimen/horizontal_page_margin"
android:layout_marginTop="@dimen/vertical_page_margin"
android:layout_marginBottom="@dimen/vertical_page_margin"
android:text="@string/intro_message" />
</LinearLayout>
</LinearLayout>

View File

@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.adaptertransition"
android:versionCode="1"
android:versionName="1.0">
<!-- Min/target SDK versions (<uses-sdk>) managed by build.gradle -->
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name="com.example.android.adaptertransition.MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -1,9 +0,0 @@
page.tags="AdapterTransition"
sample.group=UI
@jd:body
<p>
Transition cannot be directly applied to AdapterViews. In this sample, we demonstrate how to create an overlay layout and run a Transition on it. Press the action bar button to toggle between ListView and GridView.
</p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 412 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 496 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 555 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<RelativeLayout
android:id="@+id/meat_container"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="150dp">
<ImageView
android:id="@+id/meat_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
tools:src="@drawable/p1"/>
<TextView
android:id="@+id/meat_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_gravity="bottom|end"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:gravity="center_horizontal"
android:shadowColor="#000000"
android:shadowDx="0"
android:shadowDy="0"
android:shadowRadius="10"
android:textColor="#ffffff"
android:textSize="24sp"
android:textStyle="bold"
tools:text="Hello"/>
</RelativeLayout>

View File

@@ -1,46 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<RelativeLayout
android:id="@+id/meat_container"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingStart="?android:attr/listPreferredItemPaddingStart">
<ImageView
android:id="@+id/meat_image"
android:layout_width="64dp"
android:layout_height="64dp"
android:scaleType="centerCrop"
tools:src="@drawable/p1"/>
<TextView
android:id="@+id/meat_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="?android:attr/listPreferredItemPaddingStart"
android:layout_toEndOf="@id/meat_image"
android:layout_centerInParent="true"
android:gravity="center_vertical"
android:textSize="24sp"
tools:text="Title"/>
</RelativeLayout>

View File

@@ -1,280 +0,0 @@
/*
* 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.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.transition.AutoTransition;
import android.transition.Scene;
import android.transition.Transition;
import android.transition.TransitionManager;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.FrameLayout;
import android.widget.GridView;
import android.widget.ListView;
import android.widget.Toast;
/**
* Main screen for AdapterTransition sample.
*/
public class AdapterTransitionFragment extends Fragment implements Transition.TransitionListener {
/**
* Since the transition framework requires all relevant views in a view hierarchy to be marked
* with IDs, we use this ID to mark the root view.
*/
private static final int ROOT_ID = 1;
/**
* A tag for saving state whether the mAbsListView is ListView or GridView.
*/
private static final String STATE_IS_LISTVIEW = "is_listview";
/**
* This is where we place our AdapterView (ListView / GridView).
*/
private FrameLayout mContent;
/**
* This is where we carry out the transition.
*/
private FrameLayout mCover;
/**
* This list shows our contents. It can be ListView or GridView, and we toggle between them
* using the transition framework.
*/
private AbsListView mAbsListView;
/**
* This is our contents.
*/
private MeatAdapter mAdapter;
public static AdapterTransitionFragment newInstance() {
return new AdapterTransitionFragment();
}
public AdapterTransitionFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// If savedInstanceState is available, we restore the state whether the list is a ListView
// or a GridView.
boolean isListView;
if (null == savedInstanceState) {
isListView = true;
} else {
isListView = savedInstanceState.getBoolean(STATE_IS_LISTVIEW, true);
}
inflateAbsList(inflater, container, isListView);
return inflater.inflate(R.layout.fragment_adapter_transition, container, false);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(STATE_IS_LISTVIEW, mAbsListView instanceof ListView);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// Retaining references for FrameLayouts that we use later.
mContent = (FrameLayout) view.findViewById(R.id.content);
mCover = (FrameLayout) view.findViewById(R.id.cover);
// We are attaching the list to the screen here.
mContent.addView(mAbsListView);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.fragment_adapter_transition, menu);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
// We change the look of the icon every time the user toggles between list and grid.
MenuItem item = menu.findItem(R.id.action_toggle);
if (null != item) {
if (mAbsListView instanceof ListView) {
item.setIcon(R.drawable.ic_action_grid);
item.setTitle(R.string.show_as_grid);
} else {
item.setIcon(R.drawable.ic_action_list);
item.setTitle(R.string.show_as_list);
}
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_toggle: {
toggle();
return true;
}
}
return false;
}
@Override
public void onTransitionStart(Transition transition) {
}
// BEGIN_INCLUDE(on_transition_end)
@Override
public void onTransitionEnd(Transition transition) {
// When the transition ends, we remove all the views from the overlay and hide it.
mCover.removeAllViews();
mCover.setVisibility(View.INVISIBLE);
}
// END_INCLUDE(on_transition_end)
@Override
public void onTransitionCancel(Transition transition) {
}
@Override
public void onTransitionPause(Transition transition) {
}
@Override
public void onTransitionResume(Transition transition) {
}
/**
* Inflate a ListView or a GridView with a corresponding ListAdapter.
*
* @param inflater The LayoutInflater.
* @param container The ViewGroup that contains this AbsListView. The AbsListView won't be
* attached to it.
* @param inflateListView Pass true to inflate a ListView, or false to inflate a GridView.
*/
private void inflateAbsList(LayoutInflater inflater, ViewGroup container,
boolean inflateListView) {
if (inflateListView) {
mAbsListView = (AbsListView) inflater.inflate(R.layout.fragment_meat_list,
container, false);
mAdapter = new MeatAdapter(inflater, R.layout.item_meat_list);
} else {
mAbsListView = (AbsListView) inflater.inflate(R.layout.fragment_meat_grid,
container, false);
mAdapter = new MeatAdapter(inflater, R.layout.item_meat_grid);
}
mAbsListView.setAdapter(mAdapter);
mAbsListView.setOnItemClickListener(mAdapter);
}
/**
* Toggle the UI between ListView and GridView.
*/
private void toggle() {
// We use mCover as the overlay on which we carry out the transition.
mCover.setVisibility(View.VISIBLE);
// This FrameLayout holds all the visible views in the current list or grid. We use this as
// the starting Scene of the Transition later.
FrameLayout before = copyVisibleViews();
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
mCover.addView(before, params);
// Swap the actual list.
swapAbsListView();
// We also swap the icon for the toggle button.
ActivityCompat.invalidateOptionsMenu(getActivity());
// It is now ready to start the transition.
mAbsListView.post(new Runnable() {
@Override
public void run() {
// BEGIN_INCLUDE(transition_with_listener)
Scene scene = new Scene(mCover, copyVisibleViews());
Transition transition = new AutoTransition();
transition.addListener(AdapterTransitionFragment.this);
TransitionManager.go(scene, transition);
// END_INCLUDE(transition_with_listener)
}
});
}
/**
* Swap ListView with GridView, or GridView with ListView.
*/
private void swapAbsListView() {
// We save the current scrolling position before removing the current list.
int first = mAbsListView.getFirstVisiblePosition();
// If the current list is a GridView, we replace it with a ListView. If it is a ListView,
// a GridView.
LayoutInflater inflater = LayoutInflater.from(getActivity());
inflateAbsList(inflater, (ViewGroup) mAbsListView.getParent(),
mAbsListView instanceof GridView);
mAbsListView.setAdapter(mAdapter);
// We restore the scrolling position here.
mAbsListView.setSelection(first);
// The new list is ready, and we replace the existing one with it.
mContent.removeAllViews();
mContent.addView(mAbsListView);
}
/**
* Copy all the visible views in the mAbsListView into a new FrameLayout and return it.
*
* @return a FrameLayout with all the visible views inside.
*/
private FrameLayout copyVisibleViews() {
// This is the FrameLayout we return afterwards.
FrameLayout layout = new FrameLayout(getActivity());
// The transition framework requires to set ID for all views to be animated.
layout.setId(ROOT_ID);
// We only copy visible views.
int first = mAbsListView.getFirstVisiblePosition();
int index = 0;
while (true) {
// This is one of the views that we copy. Note that the argument for getChildAt is a
// zero-oriented index, and it doesn't usually match with its position in the list.
View source = mAbsListView.getChildAt(index);
if (null == source) {
break;
}
// This is the copy of the original view.
View destination = mAdapter.getView(first + index, null, layout);
assert destination != null;
destination.setId(ROOT_ID + first + index);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
source.getWidth(), source.getHeight());
params.leftMargin = (int) source.getX();
params.topMargin = (int) source.getY();
layout.addView(destination, params);
++index;
}
return layout;
}
}

View File

@@ -1,46 +0,0 @@
/*
* 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"),
};
}

View File

@@ -1,102 +0,0 @@
/*
* 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.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
/**
* 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 implements AbsListView.OnItemClickListener {
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;
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ViewHolder holder = (ViewHolder) view.getTag();
Context context = view.getContext();
if (null != holder && null != holder.title && null != context) {
Toast.makeText(context, context.getString(R.string.item_clicked,
holder.title.getText()), Toast.LENGTH_SHORT).show();
}
}
private static class ViewHolder {
public ImageView image;
public TextView title;
}
}

View File

@@ -1,314 +0,0 @@
/*
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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;
}
}
}
}
}

View File

@@ -1,208 +0,0 @@
/*
* 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;
}
}
}

View File

@@ -1,36 +0,0 @@
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout style="@style/Widget.SampleMessageTile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView style="@style/Widget.SampleMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/horizontal_page_margin"
android:layout_marginRight="@dimen/horizontal_page_margin"
android:layout_marginTop="@dimen/vertical_page_margin"
android:layout_marginBottom="@dimen/vertical_page_margin"
android:text="@string/intro_message" />
</LinearLayout>
</LinearLayout>

View File

@@ -1,36 +0,0 @@
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout style="@style/Widget.SampleMessageTile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView style="@style/Widget.SampleMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/horizontal_page_margin"
android:layout_marginRight="@dimen/horizontal_page_margin"
android:layout_marginTop="@dimen/vertical_page_margin"
android:layout_marginBottom="@dimen/vertical_page_margin"
android:text="@string/intro_message" />
</LinearLayout>
</LinearLayout>

View File

@@ -14,26 +14,21 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/vertical_page_margin"
android:paddingLeft="@dimen/horizontal_page_margin"
android:paddingRight="@dimen/horizontal_page_margin"
android:paddingTop="@dimen/vertical_page_margin">
<TextView
android:id="@+id/status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/status_not_installed" />
<Button
android:id="@+id/unhide"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/unhide" />
<Switch
android:id="@+id/say_hello"
@@ -41,4 +36,89 @@
android:layout_height="wrap_content"
android:text="@string/allow_saying_hello"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/vertical_page_margin"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@id/message"
android:text="@string/message"/>
<EditText
android:id="@id/message"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="text"
android:maxLines="1"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/vertical_page_margin"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@id/number"
android:text="@string/number"/>
<EditText
android:id="@id/number"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="number"
android:maxLines="1"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/vertical_page_margin"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/rank"/>
<Spinner
android:id="@+id/rank"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_small"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/vertical_page_margin"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/approvals"/>
<LinearLayout
android:id="@+id/approvals"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_small"
android:orientation="vertical"/>
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@@ -20,7 +20,7 @@
android:layout_height="match_parent"
tools:context="com.example.android.basicmanagedprofile.MainActivity.MainFragment">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"

View File

@@ -14,23 +14,26 @@ 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.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.android.adaptertransition.AdapterTransitionFragment">
android:paddingBottom="@dimen/vertical_page_margin"
android:paddingLeft="@dimen/horizontal_page_margin"
android:paddingRight="@dimen/horizontal_page_margin"
android:paddingTop="@dimen/vertical_page_margin">
<FrameLayout
android:id="@+id/content"
<TextView
android:id="@+id/status"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="wrap_content"
android:text="@string/status_not_installed"/>
<FrameLayout
android:id="@+id/cover"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f3f3f3"
android:visibility="invisible"/>
<Button
android:id="@+id/unhide"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/unhide"/>
</FrameLayout>
</LinearLayout>

View File

@@ -14,10 +14,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_toggle"
android:icon="@drawable/ic_action_grid"
android:showAsAction="always|withText"
android:title="@string/show_as_grid"/>
</menu>
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="@dimen/margin_medium"
android:layout_marginTop="@dimen/margin_medium"
android:background="#9000"/>

View File

@@ -14,8 +14,8 @@ 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.
-->
<ListView
android:id="@+id/abs_list_view"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<resources>
<item name="message" type="id"/>
<item name="number" type="id"/>
<item name="approval" type="id"/>
</resources>

View File

@@ -25,4 +25,8 @@
<string name="allowed">Allowed</string>
<string name="disallowed">Disallowed</string>
<string name="profile_name">AppRestrictionEnforcer </string>
<string name="message">Message: </string>
<string name="number">Number: </string>
<string name="rank">Rank: </string>
<string name="approvals">Approvals: </string>
</resources>

View File

@@ -22,34 +22,34 @@ import android.content.Context;
import android.content.RestrictionEntry;
import android.content.RestrictionsManager;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* This fragment provides UI and functionality to set restrictions on the AppRestrictionSchema
* sample.
*/
public class AppRestrictionEnforcerFragment extends Fragment implements View.OnClickListener,
CompoundButton.OnCheckedChangeListener {
/**
* Package name of the AppRestrictionSchema sample.
*/
private static final String PACKAGE_NAME_APP_RESTRICTION_SCHEMA
= "com.example.android.apprestrictionschema";
public class AppRestrictionEnforcerFragment extends Fragment implements
CompoundButton.OnCheckedChangeListener, AdapterView.OnItemSelectedListener {
/**
* Key for {@link SharedPreferences}
@@ -62,15 +62,38 @@ public class AppRestrictionEnforcerFragment extends Fragment implements View.OnC
private static final String RESTRICTION_KEY_SAY_HELLO = "can_say_hello";
/**
* Default boolean value for "can_say_hello" restriction. The actual value is loaded in
* {@link #loadRestrictions(android.app.Activity)}.
* Key for the string restriction in AppRestrictionSchema.
*/
private boolean mDefaultValueRestrictionSayHello;
private static final String RESTRICTION_KEY_MESSAGE = "message";
/**
* Key for the integer restriction in AppRestrictionSchema.
*/
private static final String RESTRICTION_KEY_NUMBER = "number";
/**
* Key for the choice restriction in AppRestrictionSchema.
*/
private static final String RESTRICTION_KEY_RANK = "rank";
/**
* Key for the multi-select restriction in AppRestrictionSchema.
*/
private static final String RESTRICTION_KEY_APPROVALS = "approvals";
private static final String DELIMETER = ",";
/**
* Current status of the restrictions.
*/
private Bundle mCurrentRestrictions = new Bundle();
// UI Components
private TextView mTextStatus;
private Button mButtonUnhide;
private Switch mSwitchSayHello;
private EditText mEditMessage;
private EditText mEditNumber;
private Spinner mSpinnerRank;
private LinearLayout mLayoutApprovals;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -80,99 +103,72 @@ public class AppRestrictionEnforcerFragment extends Fragment implements View.OnC
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
mTextStatus = (TextView) view.findViewById(R.id.status);
mButtonUnhide = (Button) view.findViewById(R.id.unhide);
// Retain references for the UI elements
mSwitchSayHello = (Switch) view.findViewById(R.id.say_hello);
mButtonUnhide.setOnClickListener(this);
mSwitchSayHello.setOnCheckedChangeListener(this);
mEditMessage = (EditText) view.findViewById(R.id.message);
mEditNumber = (EditText) view.findViewById(R.id.number);
mSpinnerRank = (Spinner) view.findViewById(R.id.rank);
mLayoutApprovals = (LinearLayout) view.findViewById(R.id.approvals);
}
@Override
public void onResume() {
super.onResume();
updateUi(getActivity());
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.unhide: {
unhideApp(getActivity());
break;
}
}
loadRestrictions(getActivity());
}
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
switch (compoundButton.getId()) {
case R.id.say_hello: {
allowSayHello(getActivity(), checked);
saveCanSayHello(getActivity(), checked);
break;
}
case R.id.approval: {
if (checked) {
addApproval(getActivity(), (String) compoundButton.getTag());
} else {
removeApproval(getActivity(), (String) compoundButton.getTag());
}
break;
}
}
}
/**
* Updates the UI components according to the current status of AppRestrictionSchema and its
* restriction.
*
* @param activity The activity
*/
private void updateUi(Activity activity) {
PackageManager packageManager = activity.getPackageManager();
private TextWatcher mWatcherMessage = new EasyTextWatcher() {
@Override
public void afterTextChanged(Editable s) {
saveMessage(getActivity(), s.toString());
}
};
private TextWatcher mWatcherNumber = new EasyTextWatcher() {
@Override
public void afterTextChanged(Editable s) {
try {
ApplicationInfo info = packageManager.getApplicationInfo(
PACKAGE_NAME_APP_RESTRICTION_SCHEMA, PackageManager.GET_UNINSTALLED_PACKAGES);
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
if (0 < (info.flags & ApplicationInfo.FLAG_INSTALLED)) {
if (!devicePolicyManager.isApplicationHidden(
EnforcerDeviceAdminReceiver.getComponentName(activity),
PACKAGE_NAME_APP_RESTRICTION_SCHEMA)) {
// The app is ready
loadRestrictions(activity);
mTextStatus.setVisibility(View.GONE);
mButtonUnhide.setVisibility(View.GONE);
mSwitchSayHello.setVisibility(View.VISIBLE);
mSwitchSayHello.setOnCheckedChangeListener(null);
mSwitchSayHello.setChecked(canSayHello(activity));
mSwitchSayHello.setOnCheckedChangeListener(this);
} else {
// The app is installed but hidden in this profile
mTextStatus.setText(R.string.status_not_activated);
mTextStatus.setVisibility(View.VISIBLE);
mButtonUnhide.setVisibility(View.VISIBLE);
mSwitchSayHello.setVisibility(View.GONE);
String string = s.toString();
if (!TextUtils.isEmpty(string)) {
saveNumber(getActivity(), Integer.parseInt(string));
}
} else {
// Need to reinstall the sample app
mTextStatus.setText(R.string.status_need_reinstall);
mTextStatus.setVisibility(View.VISIBLE);
mButtonUnhide.setVisibility(View.GONE);
mSwitchSayHello.setVisibility(View.GONE);
} catch (NumberFormatException e) {
Toast.makeText(getActivity(), "Not an integer!", Toast.LENGTH_SHORT).show();
}
}
};
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
switch (parent.getId()) {
case R.id.rank: {
saveRank(getActivity(), (String) parent.getAdapter().getItem(position));
break;
}
} catch (PackageManager.NameNotFoundException e) {
mTextStatus.setText(R.string.status_not_installed);
mTextStatus.setVisibility(View.VISIBLE);
mButtonUnhide.setVisibility(View.GONE);
mSwitchSayHello.setVisibility(View.GONE);
}
}
/**
* Unhides the AppRestrictionSchema sample in case it is hidden in this profile.
*
* @param activity The activity
*/
private void unhideApp(Activity activity) {
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
devicePolicyManager.setApplicationHidden(
EnforcerDeviceAdminReceiver.getComponentName(activity),
PACKAGE_NAME_APP_RESTRICTION_SCHEMA, false);
Toast.makeText(activity, "Enabled the app", Toast.LENGTH_SHORT).show();
updateUi(activity);
@Override
public void onNothingSelected(AdapterView<?> parent) {
// Nothing to do
}
/**
@@ -182,50 +178,187 @@ public class AppRestrictionEnforcerFragment extends Fragment implements View.OnC
* @param activity The activity
*/
private void loadRestrictions(Activity activity) {
RestrictionsManager restrictionsManager =
RestrictionsManager manager =
(RestrictionsManager) activity.getSystemService(Context.RESTRICTIONS_SERVICE);
List<RestrictionEntry> restrictions =
restrictionsManager.getManifestRestrictions(PACKAGE_NAME_APP_RESTRICTION_SCHEMA);
for (RestrictionEntry restriction : restrictions) {
if (RESTRICTION_KEY_SAY_HELLO.equals(restriction.getKey())) {
mDefaultValueRestrictionSayHello = restriction.getSelectedState();
}
}
}
/**
* Returns whether the AppRestrictionSchema is currently allowed to say hello to its user. Note
* that a profile/device owner needs to remember each restriction value on its own.
*
* @param activity The activity
* @return True if the AppRestrictionSchema is allowed to say hello
*/
private boolean canSayHello(Activity activity) {
manager.getManifestRestrictions(Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA);
SharedPreferences prefs = activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE);
return prefs.getBoolean(RESTRICTION_KEY_SAY_HELLO, mDefaultValueRestrictionSayHello);
for (RestrictionEntry restriction : restrictions) {
String key = restriction.getKey();
if (RESTRICTION_KEY_SAY_HELLO.equals(key)) {
updateCanSayHello(prefs.getBoolean(RESTRICTION_KEY_SAY_HELLO,
restriction.getSelectedState()));
} else if (RESTRICTION_KEY_MESSAGE.equals(key)) {
updateMessage(prefs.getString(RESTRICTION_KEY_MESSAGE,
restriction.getSelectedString()));
} else if (RESTRICTION_KEY_NUMBER.equals(key)) {
updateNumber(prefs.getInt(RESTRICTION_KEY_NUMBER,
restriction.getIntValue()));
} else if (RESTRICTION_KEY_RANK.equals(key)) {
updateRank(activity, restriction.getChoiceValues(),
prefs.getString(RESTRICTION_KEY_RANK, restriction.getSelectedString()));
} else if (RESTRICTION_KEY_APPROVALS.equals(key)) {
updateApprovals(activity, restriction.getChoiceValues(),
TextUtils.split(prefs.getString(RESTRICTION_KEY_APPROVALS,
TextUtils.join(DELIMETER,
restriction.getAllSelectedStrings())),
DELIMETER));
}
}
}
private void updateCanSayHello(boolean canSayHello) {
mCurrentRestrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, canSayHello);
mSwitchSayHello.setOnCheckedChangeListener(null);
mSwitchSayHello.setChecked(canSayHello);
mSwitchSayHello.setOnCheckedChangeListener(this);
}
private void updateMessage(String message) {
mCurrentRestrictions.putString(RESTRICTION_KEY_MESSAGE, message);
mEditMessage.removeTextChangedListener(mWatcherMessage);
mEditMessage.setText(message);
mEditMessage.addTextChangedListener(mWatcherMessage);
}
private void updateNumber(int number) {
mCurrentRestrictions.putInt(RESTRICTION_KEY_NUMBER, number);
mEditNumber.removeTextChangedListener(mWatcherNumber);
mEditNumber.setText(String.valueOf(number));
mEditNumber.addTextChangedListener(mWatcherNumber);
}
private void updateRank(Context context, String[] ranks, String selectedRank) {
mCurrentRestrictions.putString(RESTRICTION_KEY_RANK, selectedRank);
mSpinnerRank.setAdapter(new ArrayAdapter<>(context,
android.R.layout.simple_spinner_dropdown_item, ranks));
mSpinnerRank.setSelection(search(ranks, selectedRank));
mSpinnerRank.setOnItemSelectedListener(this);
}
private void updateApprovals(Context context, String[] approvals,
String[] selectedApprovals) {
mCurrentRestrictions.putStringArray(RESTRICTION_KEY_APPROVALS, selectedApprovals);
mLayoutApprovals.removeAllViews();
for (String approval : approvals) {
Switch sw = new Switch(context);
sw.setText(approval);
sw.setTag(approval);
sw.setChecked(Arrays.asList(selectedApprovals).contains(approval));
sw.setOnCheckedChangeListener(this);
sw.setId(R.id.approval);
mLayoutApprovals.addView(sw);
}
}
/**
* Sets the value for the "cay_say_hello" restriction of AppRestrictionSchema.
* Saves the value for the "cay_say_hello" restriction of AppRestrictionSchema.
*
* @param activity The activity
* @param allow The value to be set for the restriction.
*/
private void allowSayHello(Activity activity, boolean allow) {
private void saveCanSayHello(Activity activity, boolean allow) {
mCurrentRestrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, allow);
saveRestrictions(activity);
// Note that the owner app needs to remember the restrictions on its own.
editPreferences(activity).putBoolean(RESTRICTION_KEY_SAY_HELLO, allow).apply();
}
/**
* Saves the value for the "message" restriction of AppRestrictionSchema.
*
* @param activity The activity
* @param message The value to be set for the restriction.
*/
private void saveMessage(Activity activity, String message) {
mCurrentRestrictions.putString(RESTRICTION_KEY_MESSAGE, message);
saveRestrictions(activity);
editPreferences(activity).putString(RESTRICTION_KEY_MESSAGE, message).apply();
}
/**
* Saves the value for the "number" restriction of AppRestrictionSchema.
*
* @param activity The activity
* @param number The value to be set for the restriction.
*/
private void saveNumber(Activity activity, int number) {
mCurrentRestrictions.putInt(RESTRICTION_KEY_NUMBER, number);
saveRestrictions(activity);
editPreferences(activity).putInt(RESTRICTION_KEY_NUMBER, number).apply();
}
/**
* Saves the value for the "rank" restriction of AppRestrictionSchema.
*
* @param activity The activity
* @param rank The value to be set for the restriction.
*/
private void saveRank(Activity activity, String rank) {
mCurrentRestrictions.putString(RESTRICTION_KEY_RANK, rank);
saveRestrictions(activity);
editPreferences(activity).putString(RESTRICTION_KEY_RANK, rank).apply();
}
private void addApproval(Activity activity, String approval) {
List<String> approvals = new ArrayList<>(Arrays.asList(
mCurrentRestrictions.getStringArray(RESTRICTION_KEY_APPROVALS)));
if (approvals.contains(approval)) {
return;
}
approvals.add(approval);
saveApprovals(activity, approvals.toArray(new String[approvals.size()]));
}
private void removeApproval(Activity activity, String approval) {
List<String> approvals = new ArrayList<>(Arrays.asList(
mCurrentRestrictions.getStringArray(RESTRICTION_KEY_APPROVALS)));
if (!approval.contains(approval)) {
return;
}
approvals.remove(approval);
saveApprovals(activity, approvals.toArray(new String[approvals.size()]));
}
/**
* Saves the value for the "approvals" restriction of AppRestrictionSchema.
*
* @param activity The activity
* @param approvals The value to be set for the restriction.
*/
private void saveApprovals(Activity activity, String[] approvals) {
mCurrentRestrictions.putStringArray(RESTRICTION_KEY_APPROVALS, approvals);
saveRestrictions(activity);
editPreferences(activity).putString(RESTRICTION_KEY_APPROVALS,
TextUtils.join(DELIMETER, approvals)).apply();
}
private void saveRestrictions(Activity activity) {
DevicePolicyManager devicePolicyManager
= (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
Bundle restrictions = new Bundle();
restrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, allow);
devicePolicyManager.setApplicationRestrictions(
EnforcerDeviceAdminReceiver.getComponentName(activity),
PACKAGE_NAME_APP_RESTRICTION_SCHEMA, restrictions);
// The profile/device owner needs to remember the current state of restrictions on its own
activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE)
.edit()
.putBoolean(RESTRICTION_KEY_SAY_HELLO, allow)
.apply();
Toast.makeText(activity, allow ? R.string.allowed : R.string.disallowed,
Toast.LENGTH_SHORT).show();
Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA, mCurrentRestrictions);
}
private SharedPreferences.Editor editPreferences(Activity activity) {
return activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE).edit();
}
/**
* Sequential search
*
* @param array The string array
* @param s The string to search for
* @return Index if found. -1 if not found.
*/
private int search(String[] array, String s) {
for (int i = 0; i < array.length; ++i) {
if (s.equals(array[i])) {
return i;
}
}
return -1;
}
}

View File

@@ -14,16 +14,14 @@
* limitations under the License.
*/
package com.example.android.wearable.embeddedapp;
package com.example.android.apprestrictionenforcer;
import android.app.Activity;
import android.os.Bundle;
public interface Constants {
public class PhoneActivity extends Activity {
/**
* Package name of the AppRestrictionSchema sample.
*/
public static final String PACKAGE_NAME_APP_RESTRICTION_SCHEMA
= "com.example.android.apprestrictionschema";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_phone);
}
}

View File

@@ -14,16 +14,25 @@
* limitations under the License.
*/
package com.example.android.wearable.embeddedapp;
package com.example.android.apprestrictionenforcer;
import android.app.Activity;
import android.os.Bundle;
import android.text.TextWatcher;
public class WearableActivity extends Activity {
/**
* This is a wrapper around {@link TextWatcher} that overrides
* {@link TextWatcher#beforeTextChanged(CharSequence, int, int, int)} and
* {@link TextWatcher#onTextChanged(CharSequence, int, int, int)} with empty bodies.
*/
public abstract class EasyTextWatcher implements TextWatcher {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wearable);
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Do nothing
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Do nothing
}
}

View File

@@ -18,24 +18,44 @@ package com.example.android.apprestrictionenforcer;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
public class MainActivity extends FragmentActivity {
public class MainActivity extends FragmentActivity implements StatusFragment.StatusUpdatedListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_real);
if (null == savedInstanceState) {
DevicePolicyManager manager = (DevicePolicyManager)
getSystemService(Context.DEVICE_POLICY_SERVICE);
if (manager.isProfileOwnerApp(getApplicationContext().getPackageName())) {
// If the managed profile is already set up, we show the main screen.
showMainFragment();
} else {
// If not, we show the set up screen.
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
PackageManager packageManager = getPackageManager();
if (!devicePolicyManager.isProfileOwnerApp(getApplicationContext().getPackageName())) {
// If the managed profile is not yet set up, we show the setup screen.
showSetupProfile();
} else {
try {
ApplicationInfo info = packageManager.getApplicationInfo(
Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA,
PackageManager.GET_UNINSTALLED_PACKAGES);
if (0 == (info.flags & ApplicationInfo.FLAG_INSTALLED)) {
// Need to reinstall the sample app
showStatusProfile();
} else if (devicePolicyManager.isApplicationHidden(
EnforcerDeviceAdminReceiver.getComponentName(this),
Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA)) {
// The app is installed but hidden in this profile
showStatusProfile();
} else {
// Everything is clear; show the main screen
showMainFragment();
}
} catch (PackageManager.NameNotFoundException e) {
showStatusProfile();
}
}
}
}
@@ -46,10 +66,21 @@ public class MainActivity extends FragmentActivity {
.commit();
}
private void showStatusProfile() {
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, new StatusFragment())
.commit();
}
private void showMainFragment() {
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, new AppRestrictionEnforcerFragment())
.commit();
}
@Override
public void onStatusUpdated() {
showMainFragment();
}
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 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.apprestrictionenforcer;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
/**
* Provides UI for enabling the target app in this profile. The status of the app can be
* uninstalled, hidden, or enabled depending on the situations. This fragment shows suitable
* controls for each status.
*/
public class StatusFragment extends Fragment implements View.OnClickListener {
private TextView mTextStatus;
private Button mButtonUnhide;
private StatusUpdatedListener mListener;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_status, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
mTextStatus = (TextView) view.findViewById(R.id.status);
mButtonUnhide = (Button) view.findViewById(R.id.unhide);
mButtonUnhide.setOnClickListener(this);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mListener = (StatusUpdatedListener) activity;
}
@Override
public void onDetach() {
mListener = null;
super.onDetach();
}
@Override
public void onResume() {
super.onResume();
updateUi(getActivity());
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.unhide: {
unhideApp(getActivity());
break;
}
}
}
private void updateUi(Activity activity) {
PackageManager packageManager = activity.getPackageManager();
try {
ApplicationInfo info = packageManager.getApplicationInfo(
Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA,
PackageManager.GET_UNINSTALLED_PACKAGES);
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
if ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
if (!devicePolicyManager.isApplicationHidden(
EnforcerDeviceAdminReceiver.getComponentName(activity),
Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA)) {
// The app is ready to enforce restrictions
// This is unlikely to happen in this sample as unhideApp() handles it.
mListener.onStatusUpdated();
} else {
// The app is installed but hidden in this profile
mTextStatus.setText(R.string.status_not_activated);
mButtonUnhide.setVisibility(View.VISIBLE);
}
} else {
// Need to reinstall the sample app
mTextStatus.setText(R.string.status_need_reinstall);
mButtonUnhide.setVisibility(View.GONE);
}
} catch (PackageManager.NameNotFoundException e) {
// Need to reinstall the sample app
mTextStatus.setText(R.string.status_need_reinstall);
mButtonUnhide.setVisibility(View.GONE);
}
}
/**
* Unhides the AppRestrictionSchema sample in case it is hidden in this profile.
*
* @param activity The activity
*/
private void unhideApp(Activity activity) {
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
devicePolicyManager.setApplicationHidden(
EnforcerDeviceAdminReceiver.getComponentName(activity),
Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA, false);
Toast.makeText(activity, "Enabled the app", Toast.LENGTH_SHORT).show();
mListener.onStatusUpdated();
}
public interface StatusUpdatedListener {
public void onStatusUpdated();
}
}

View File

@@ -14,9 +14,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/margin_medium">
@@ -24,8 +31,8 @@ limitations under the License.
android:id="@+id/say_hello_explanation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/explanation_can_say_hello_true"
android:textAppearance="?android:attr/textAppearanceMedium" />
android:textAppearance="?android:attr/textAppearanceMedium"
tools:text="@string/explanation_can_say_hello_true"/>
<Button
android:id="@+id/say_hello"
@@ -34,4 +41,33 @@ limitations under the License.
android:layout_marginStart="@dimen/margin_medium"
android:text="@string/action_can_say_hello"/>
<include layout="@layout/separator"/>
<TextView
android:id="@+id/your_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
tools:text="@string/your_number"/>
<include layout="@layout/separator"/>
<TextView
android:id="@+id/your_rank"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
tools:text="@string/your_rank"/>
<include layout="@layout/separator"/>
<TextView
android:id="@+id/approvals_you_have"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
tools:text="@string/approvals_you_have"/>
</LinearLayout>
</ScrollView>

View File

@@ -14,16 +14,9 @@ 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.
-->
<GridView
android:id="@+id/abs_list_view"
xmlns:android="http://schemas.android.com/apk/res/android"
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:columnWidth="150dp"
android:horizontalSpacing="1dp"
android:numColumns="auto_fit"
android:padding="1dp"
android:scrollbars="none"
android:stretchMode="columnWidth"
android:verticalSpacing="1dp"/>
android:layout_height="1dp"
android:layout_marginBottom="@dimen/margin_medium"
android:layout_marginTop="@dimen/margin_medium"
android:background="#9000"/>

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<resources>
<!-- Bool restriction -->
<string name="title_can_say_hello">Can say hello</string>
<string name="description_can_say_hello">Whether the app can say hello to the user</string>
<bool name="default_can_say_hello">false</bool>
<!-- String restriction -->
<string name="title_message">Message</string>
<string name="description_message">A message string to show</string>
<string name="default_message">Hello!</string>
<!-- Integer restriction -->
<string name="title_number">Number</string>
<string name="description_number">Sample integer value</string>
<integer name="default_number">32582657</integer>
<!-- Choice restriction -->
<string name="title_rank">Rank</string>
<string name="description_rank">Rank of the user</string>
<string-array name="entries_rank">
<item>Apprentice</item>
<item>Intermediate</item>
<item>Master</item>
</string-array>
<string name="entry_value_rank_apprentice">apprentice</string>
<string name="entry_value_rank_intermediate">intermediate</string>
<string name="entry_value_rank_master">master</string>
<string-array name="entry_values_rank">
<item>@string/entry_value_rank_apprentice</item>
<item>@string/entry_value_rank_intermediate</item>
<item>@string/entry_value_rank_master</item>
</string-array>
<string name="default_rank">@string/entry_value_rank_apprentice</string>
<!-- Multi-select restriction -->
<string name="title_approvals">Approvals</string>
<string name="description_approvals">Approvals</string>
<string-array name="entries_approvals">
<item>Read</item>
<item>Write</item>
<item>Execute</item>
</string-array>
<string name="entry_value_approvals_read">read</string>
<string name="entry_value_approvals_write">write</string>
<string name="entry_value_approvals_execute">execute</string>
<string-array name="entry_values_approvals">
<item>@string/entry_value_approvals_read</item>
<item>@string/entry_value_approvals_write</item>
<item>@string/entry_value_approvals_execute</item>
</string-array>
<string-array name="default_approvals">
<!-- Empty -->
</string-array>
<!-- Hidden restriction -->
<string name="title_secret_code">Secret code</string>
<string name="description_secret_code">This restriction is hidden and will not be shown to the administrator.</string>
<string name="default_secret_code">(Hidden restriction must have some default value)</string>
</resources>

View File

@@ -16,11 +16,14 @@ limitations under the License.
-->
<resources>
<string name="title_can_say_hello">Can say hello</string>
<string name="description_can_say_hello">Whether the app can say hello to the user</string>
<string name="explanation_can_say_hello_true">I can say hello to you.</string>
<string name="explanation_can_say_hello_false">I am restricted from saying hello to you.</string>
<string name="action_can_say_hello">Say hello</string>
<string name="message_hello">Hello!</string>
<string name="message">All I can say is \"%s\".</string>
<string name="your_number">Your number: %d</string>
<string name="your_rank">Your rank: %s</string>
<string name="approvals_you_have">Approvals you have: %s</string>
<string name="none">none</string>
</resources>

View File

@@ -16,11 +16,55 @@ limitations under the License.
-->
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
<!--
Refer to the javadoc of RestrictionsManager for detail of this file.
https://developer.android.com/reference/android/content/RestrictionsManager.html
-->
<restriction
android:defaultValue="false"
android:defaultValue="@bool/default_can_say_hello"
android:description="@string/description_can_say_hello"
android:key="can_say_hello"
android:restrictionType="bool"
android:title="@string/title_can_say_hello"/>
<restriction
android:defaultValue="@string/default_message"
android:description="@string/description_message"
android:key="message"
android:restrictionType="string"
android:title="@string/title_message"/>
<restriction
android:defaultValue="@integer/default_number"
android:description="@string/description_number"
android:key="number"
android:restrictionType="integer"
android:title="@string/title_number"/>
<restriction
android:defaultValue="@string/default_rank"
android:description="@string/description_rank"
android:entries="@array/entries_rank"
android:entryValues="@array/entry_values_rank"
android:key="rank"
android:restrictionType="choice"
android:title="@string/title_rank"/>
<restriction
android:defaultValue="@array/default_approvals"
android:description="@string/description_approvals"
android:entries="@array/entries_approvals"
android:entryValues="@array/entry_values_approvals"
android:key="approvals"
android:restrictionType="multi-select"
android:title="@string/title_approvals"/>
<restriction
android:defaultValue="@string/default_secret_code"
android:description="@string/description_secret_code"
android:key="secret_code"
android:restrictionType="hidden"
android:title="@string/title_secret_code"/>
</restrictions>

View File

@@ -17,10 +17,12 @@
package com.example.android.apprestrictionschema;
import android.content.Context;
import android.content.RestrictionEntry;
import android.content.RestrictionsManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -30,6 +32,8 @@ import android.widget.Toast;
import com.example.android.common.logger.Log;
import java.util.List;
/**
* Pressing the button on this fragment pops up a simple Toast message. The button is enabled or
* disabled according to the restrictions set by device/profile owner. You can use the
@@ -40,9 +44,21 @@ public class AppRestrictionSchemaFragment extends Fragment implements View.OnCli
// Tag for the logger
private static final String TAG = "AppRestrictionSchemaFragment";
private static final String KEY_CAN_SAY_HELLO = "can_say_hello";
private static final String KEY_MESSAGE = "message";
private static final String KEY_NUMBER = "number";
private static final String KEY_RANK = "rank";
private static final String KEY_APPROVALS = "approvals";
// Message to show when the button is clicked (String restriction)
private String mMessage;
// UI Components
private TextView mTextSayHello;
private Button mButtonSayHello;
private TextView mTextNumber;
private TextView mTextRank;
private TextView mTextApprovals;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -54,48 +70,103 @@ public class AppRestrictionSchemaFragment extends Fragment implements View.OnCli
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
mTextSayHello = (TextView) view.findViewById(R.id.say_hello_explanation);
mButtonSayHello = (Button) view.findViewById(R.id.say_hello);
mTextNumber = (TextView) view.findViewById(R.id.your_number);
mTextRank = (TextView) view.findViewById(R.id.your_rank);
mTextApprovals = (TextView) view.findViewById(R.id.approvals_you_have);
mButtonSayHello.setOnClickListener(this);
}
@Override
public void onResume() {
super.onResume();
// Update the UI according to the configured restrictions
RestrictionsManager restrictionsManager =
resolveRestrictions();
}
private void resolveRestrictions() {
RestrictionsManager manager =
(RestrictionsManager) getActivity().getSystemService(Context.RESTRICTIONS_SERVICE);
Bundle restrictions = restrictionsManager.getApplicationRestrictions();
updateUI(restrictions);
Bundle restrictions = manager.getApplicationRestrictions();
List<RestrictionEntry> entries = manager.getManifestRestrictions(getActivity().getApplicationContext().getPackageName());
for (RestrictionEntry entry : entries) {
String key = entry.getKey();
Log.d(TAG, "key: " + key);
if (key.equals(KEY_CAN_SAY_HELLO)) {
updateCanSayHello(entry, restrictions);
} else if (key.equals(KEY_MESSAGE)) {
updateMessage(entry, restrictions);
} else if (key.equals(KEY_NUMBER)) {
updateNumber(entry, restrictions);
} else if (key.equals(KEY_RANK)) {
updateRank(entry, restrictions);
} else if (key.equals(KEY_APPROVALS)) {
updateApprovals(entry, restrictions);
}
}
}
private void updateUI(Bundle restrictions) {
if (canSayHello(restrictions)) {
mTextSayHello.setText(R.string.explanation_can_say_hello_true);
mButtonSayHello.setEnabled(true);
private void updateCanSayHello(RestrictionEntry entry, Bundle restrictions) {
boolean canSayHello;
if (restrictions == null || !restrictions.containsKey(KEY_CAN_SAY_HELLO)) {
canSayHello = entry.getSelectedState();
} else {
mTextSayHello.setText(R.string.explanation_can_say_hello_false);
mButtonSayHello.setEnabled(false);
canSayHello = restrictions.getBoolean(KEY_CAN_SAY_HELLO);
}
mTextSayHello.setText(canSayHello ?
R.string.explanation_can_say_hello_true :
R.string.explanation_can_say_hello_false);
mButtonSayHello.setEnabled(canSayHello);
}
private void updateMessage(RestrictionEntry entry, Bundle restrictions) {
if (restrictions == null || !restrictions.containsKey(KEY_MESSAGE)) {
mMessage = entry.getSelectedString();
} else {
mMessage = restrictions.getString(KEY_MESSAGE);
}
}
/**
* Returns the current status of the restriction.
*
* @param restrictions The application restrictions
* @return True if the app is allowed to say hello
*/
private boolean canSayHello(Bundle restrictions) {
final boolean defaultValue = false;
boolean canSayHello = restrictions == null ? defaultValue :
restrictions.getBoolean("can_say_hello", defaultValue);
Log.d(TAG, "canSayHello: " + canSayHello);
return canSayHello;
private void updateNumber(RestrictionEntry entry, Bundle restrictions) {
int number;
if (restrictions == null || !restrictions.containsKey(KEY_NUMBER)) {
number = entry.getIntValue();
} else {
number = restrictions.getInt(KEY_NUMBER);
}
mTextNumber.setText(getString(R.string.your_number, number));
}
private void updateRank(RestrictionEntry entry, Bundle restrictions) {
String rank;
if (restrictions == null || !restrictions.containsKey(KEY_RANK)) {
rank = entry.getSelectedString();
} else {
rank = restrictions.getString(KEY_RANK);
}
mTextRank.setText(getString(R.string.your_rank, rank));
}
private void updateApprovals(RestrictionEntry entry, Bundle restrictions) {
String[] approvals;
if (restrictions == null || !restrictions.containsKey(KEY_APPROVALS)) {
approvals = entry.getAllSelectedStrings();
} else {
approvals = restrictions.getStringArray(KEY_APPROVALS);
}
String text;
if (approvals == null || approvals.length == 0) {
text = getString(R.string.none);
} else {
text = TextUtils.join(", ", approvals);
}
mTextApprovals.setText(getString(R.string.approvals_you_have, text));
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.say_hello: {
Toast.makeText(getActivity(), R.string.message_hello, Toast.LENGTH_SHORT).show();
Toast.makeText(getActivity(), getString(R.string.message, mMessage),
Toast.LENGTH_SHORT).show();
break;
}
}

View File

@@ -1,36 +0,0 @@
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout style="@style/Widget.SampleMessageTile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView style="@style/Widget.SampleMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/horizontal_page_margin"
android:layout_marginRight="@dimen/horizontal_page_margin"
android:layout_marginTop="@dimen/vertical_page_margin"
android:layout_marginBottom="@dimen/vertical_page_margin"
android:text="@string/intro_message" />
</LinearLayout>
</LinearLayout>

View File

@@ -14,17 +14,24 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.google.wearable.app">
package="com.example.android.appusagestatistics"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="18"
android:targetSdkVersion="21" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
<application android:allowBackup="true"
android:label="@string/app_name"
android:icon="@drawable/ic_launcher"
android:theme="@style/Theme.AppCompat.Light">
<activity android:name=".AppUsageStatisticsActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,10 @@
page.tags="AppUsageStatistics"
sample.group=System
@jd:body
<p>
This sample explains how to use App usage statistics API, which was introduced
in Android 5.0.
</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 196 B

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.android.appusagestatistics.AppUsageStatisticsActivity"
tools:ignore="MergeRootFrame" />

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/margin_medium">
<Button android:id="@+id/button_open_usage_setting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/open_app_usage_setting"
android:visibility="gone"
/>
<LinearLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/time_span"
/>
<Spinner android:id="@+id/spinner_time_span"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview_app_usage"
android:layout_marginLeft="@dimen/margin_small"
android:layout_marginRight="@dimen/margin_small"
android:scrollbars="vertical"
android:drawSelectorOnTop="true"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/usage_row_height"
>
<LinearLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_centerVertical="true"
android:gravity="center_vertical"
>
<ImageView android:id="@+id/app_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<LinearLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_medium"
android:orientation="vertical"
>
<TextView
android:id="@+id/textview_package_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/PackageNameFont"
/>
<LinearLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:id="@+id/textview_last_time_used_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/last_time_used"
/>
<TextView
android:id="@+id/textview_last_time_used"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<View android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#bbbbbb"/>
</RelativeLayout>

View File

@@ -15,12 +15,13 @@
limitations under the License.
-->
<resources>
<string name="app_name">FragmentTransition</string>
<string name="app_name">AppUsageStatistics</string>
<string name="intro_message">
<![CDATA[
This sample demonstrates how to start a transition right after a fragment transaction.
This sample explains how to use App usage statistics API, which was introduced
in Android 5.0.
]]>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2013 The Android Open Source Project
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.
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<dimen name="usage_row_height">72dp</dimen>
</resources>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<resources>
<string-array name="action_list">
<item>Daily</item>
<item>Weekly</item>
<item>Monthly</item>
<item>Yearly</item>
</string-array>
</resources>

View File

@@ -14,17 +14,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.wearable.flashlight">
<uses-sdk android:minSdkVersion="18"
android:targetSdkVersion="21" />
<application android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name">
</application>
</manifest>
<resources>
<string name="open_app_usage_setting">Open Apps with usage access settings</string>
<string name="last_time_used">"Last time used: "</string>
<string name="time_span">"Time span: "</string>
<string name="explanation_access_to_appusage_is_not_enabled">Failed to retrieve app usage
statistics. You may need to enable access
for this app through Settings > Security > Apps with usage access
</string>
</resources>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<resources>
<style name="PackageNameFont" parent="@android:style/TextAppearance.Small">
<item name="android:textColor">#000000</item>
</style>
</resources>

View File

@@ -0,0 +1,37 @@
/*
* 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.appusagestatistics;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
/**
* Launcher Activity for the App Usage Statistics sample app.
*/
public class AppUsageStatisticsActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_usage_statistics);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, AppUsageStatisticsFragment.newInstance())
.commit();
}
}
}

View File

@@ -0,0 +1,233 @@
/*
* 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.appusagestatistics;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Fragment that demonstrates how to use App Usage Statistics API.
*/
public class AppUsageStatisticsFragment extends Fragment {
private static final String TAG = AppUsageStatisticsFragment.class.getSimpleName();
//VisibleForTesting for variables below
UsageStatsManager mUsageStatsManager;
UsageListAdapter mUsageListAdapter;
RecyclerView mRecyclerView;
RecyclerView.LayoutManager mLayoutManager;
Button mOpenUsageSettingButton;
Spinner mSpinner;
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @return A new instance of fragment {@link AppUsageStatisticsFragment}.
*/
public static AppUsageStatisticsFragment newInstance() {
AppUsageStatisticsFragment fragment = new AppUsageStatisticsFragment();
return fragment;
}
public AppUsageStatisticsFragment() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mUsageStatsManager = (UsageStatsManager) getActivity()
.getSystemService("usagestats"); //Context.USAGE_STATS_SERVICE
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_app_usage_statistics, container, false);
}
@Override
public void onViewCreated(View rootView, Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);
mLayoutManager = new LinearLayoutManager(getActivity());
mUsageListAdapter = new UsageListAdapter();
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview_app_usage);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.scrollToPosition(0);
mRecyclerView.setAdapter(mUsageListAdapter);
mOpenUsageSettingButton = (Button) rootView.findViewById(R.id.button_open_usage_setting);
mSpinner = (Spinner) rootView.findViewById(R.id.spinner_time_span);
SpinnerAdapter spinnerAdapter = ArrayAdapter.createFromResource(getActivity(),
R.array.action_list, android.R.layout.simple_spinner_dropdown_item);
mSpinner.setAdapter(spinnerAdapter);
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
String[] strings = getResources().getStringArray(R.array.action_list);
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
StatsUsageInterval statsUsageInterval = StatsUsageInterval
.getValue(strings[position]);
if (statsUsageInterval != null) {
List<UsageStats> usageStatsList =
getUsageStatistics(statsUsageInterval.mInterval);
Collections.sort(usageStatsList, new LastTimeLaunchedComparatorDesc());
updateAppsList(usageStatsList);
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
/**
* Returns the {@link #mRecyclerView} including the time span specified by the
* intervalType argument.
*
* @param intervalType The time interval by which the stats are aggregated.
* Corresponding to the value of {@link UsageStatsManager}.
* E.g. {@link UsageStatsManager#INTERVAL_DAILY}, {@link
* UsageStatsManager#INTERVAL_WEEKLY},
*
* @return A list of {@link android.app.usage.UsageStats}.
*/
public List<UsageStats> getUsageStatistics(int intervalType) {
// Get the app statistics since one year ago from the current time.
Calendar cal = Calendar.getInstance();
cal.add(Calendar.YEAR, -1);
List<UsageStats> queryUsageStats = mUsageStatsManager
.queryUsageStats(intervalType, cal.getTimeInMillis(),
System.currentTimeMillis());
if (queryUsageStats.size() == 0) {
Log.i(TAG, "The user may not allow the access to apps usage. ");
Toast.makeText(getActivity(),
getString(R.string.explanation_access_to_appusage_is_not_enabled),
Toast.LENGTH_LONG).show();
mOpenUsageSettingButton.setVisibility(View.VISIBLE);
mOpenUsageSettingButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
}
});
}
return queryUsageStats;
}
/**
* Updates the {@link #mRecyclerView} with the list of {@link UsageStats} passed as an argument.
*
* @param usageStatsList A list of {@link UsageStats} from which update the
* {@link #mRecyclerView}.
*/
//VisibleForTesting
void updateAppsList(List<UsageStats> usageStatsList) {
List<CustomUsageStats> customUsageStatsList = new ArrayList<>();
for (int i = 0; i < usageStatsList.size(); i++) {
CustomUsageStats customUsageStats = new CustomUsageStats();
customUsageStats.usageStats = usageStatsList.get(i);
try {
Drawable appIcon = getActivity().getPackageManager()
.getApplicationIcon(customUsageStats.usageStats.getPackageName());
customUsageStats.appIcon = appIcon;
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, String.format("App Icon is not found for %s",
customUsageStats.usageStats.getPackageName()));
customUsageStats.appIcon = getActivity()
.getDrawable(R.drawable.ic_default_app_launcher);
}
customUsageStatsList.add(customUsageStats);
}
mUsageListAdapter.setCustomUsageStatsList(customUsageStatsList);
mUsageListAdapter.notifyDataSetChanged();
mRecyclerView.scrollToPosition(0);
}
/**
* The {@link Comparator} to sort a collection of {@link UsageStats} sorted by the timestamp
* last time the app was used in the descendant order.
*/
private static class LastTimeLaunchedComparatorDesc implements Comparator<UsageStats> {
@Override
public int compare(UsageStats left, UsageStats right) {
return (int) (right.getLastTimeUsed() - left.getLastTimeUsed());
}
}
/**
* Enum represents the intervals for {@link android.app.usage.UsageStatsManager} so that
* values for intervals can be found by a String representation.
*
*/
//VisibleForTesting
static enum StatsUsageInterval {
DAILY("Daily", UsageStatsManager.INTERVAL_DAILY),
WEEKLY("Weekly", UsageStatsManager.INTERVAL_WEEKLY),
MONTHLY("Monthly", UsageStatsManager.INTERVAL_MONTHLY),
YEARLY("Yearly", UsageStatsManager.INTERVAL_YEARLY);
private int mInterval;
private String mStringRepresentation;
StatsUsageInterval(String stringRepresentation, int interval) {
mStringRepresentation = stringRepresentation;
mInterval = interval;
}
static StatsUsageInterval getValue(String stringRepresentation) {
for (StatsUsageInterval statsUsageInterval : values()) {
if (statsUsageInterval.mStringRepresentation.equals(stringRepresentation)) {
return statsUsageInterval;
}
}
return null;
}
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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.appusagestatistics;
import android.app.usage.UsageStats;
import android.graphics.drawable.Drawable;
/**
* Entity class represents usage stats and app icon.
*/
public class CustomUsageStats {
public UsageStats usageStats;
public Drawable appIcon;
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 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.appusagestatistics;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Provide views to RecyclerView with the directory entries.
*/
public class UsageListAdapter extends RecyclerView.Adapter<UsageListAdapter.ViewHolder> {
private List<CustomUsageStats> mCustomUsageStatsList = new ArrayList<>();
private DateFormat mDateFormat = new SimpleDateFormat();
/**
* Provide a reference to the type of views that you are using (custom ViewHolder)
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
private final TextView mPackageName;
private final TextView mLastTimeUsed;
private final ImageView mAppIcon;
public ViewHolder(View v) {
super(v);
mPackageName = (TextView) v.findViewById(R.id.textview_package_name);
mLastTimeUsed = (TextView) v.findViewById(R.id.textview_last_time_used);
mAppIcon = (ImageView) v.findViewById(R.id.app_icon);
}
public TextView getLastTimeUsed() {
return mLastTimeUsed;
}
public TextView getPackageName() {
return mPackageName;
}
public ImageView getAppIcon() {
return mAppIcon;
}
}
public UsageListAdapter() {
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.usage_row, viewGroup, false);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
viewHolder.getPackageName().setText(
mCustomUsageStatsList.get(position).usageStats.getPackageName());
long lastTimeUsed = mCustomUsageStatsList.get(position).usageStats.getLastTimeUsed();
viewHolder.getLastTimeUsed().setText(mDateFormat.format(new Date(lastTimeUsed)));
viewHolder.getAppIcon().setImageDrawable(mCustomUsageStatsList.get(position).appIcon);
}
@Override
public int getItemCount() {
return mCustomUsageStatsList.size();
}
public void setCustomUsageStatsList(List<CustomUsageStats> customUsageStats) {
mCustomUsageStatsList = customUsageStats;
}
}

View File

@@ -1,36 +0,0 @@
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout style="@style/Widget.SampleMessageTile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView style="@style/Widget.SampleMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/horizontal_page_margin"
android:layout_marginRight="@dimen/horizontal_page_margin"
android:layout_marginTop="@dimen/vertical_page_margin"
android:layout_marginBottom="@dimen/vertical_page_margin"
android:text="@string/intro_message" />
</LinearLayout>
</LinearLayout>

View File

@@ -1,36 +0,0 @@
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout style="@style/Widget.SampleMessageTile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView style="@style/Widget.SampleMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/horizontal_page_margin"
android:layout_marginRight="@dimen/horizontal_page_margin"
android:layout_marginTop="@dimen/vertical_page_margin"
android:layout_marginBottom="@dimen/vertical_page_margin"
android:text="@string/intro_message" />
</LinearLayout>
</LinearLayout>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,36 +0,0 @@
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout style="@style/Widget.SampleMessageTile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView style="@style/Widget.SampleMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/horizontal_page_margin"
android:layout_marginRight="@dimen/horizontal_page_margin"
android:layout_marginTop="@dimen/vertical_page_margin"
android:layout_marginBottom="@dimen/vertical_page_margin"
android:text="@string/intro_message" />
</LinearLayout>
</LinearLayout>

View File

@@ -20,7 +20,7 @@
android:layout_height="match_parent"
tools:context="com.example.android.basicmanagedprofile.MainActivity.MainFragment">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"

View File

@@ -1,36 +0,0 @@
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout style="@style/Widget.SampleMessageTile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView style="@style/Widget.SampleMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/horizontal_page_margin"
android:layout_marginRight="@dimen/horizontal_page_margin"
android:layout_marginTop="@dimen/vertical_page_margin"
android:layout_marginBottom="@dimen/vertical_page_margin"
android:text="@string/intro_message" />
</LinearLayout>
</LinearLayout>

View File

@@ -1,36 +0,0 @@
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout style="@style/Widget.SampleMessageTile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView style="@style/Widget.SampleMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/horizontal_page_margin"
android:layout_marginRight="@dimen/horizontal_page_margin"
android:layout_marginTop="@dimen/vertical_page_margin"
android:layout_marginBottom="@dimen/vertical_page_margin"
android:text="@string/intro_message" />
</LinearLayout>
</LinearLayout>

View File

@@ -4,9 +4,9 @@ sample.group=Input
<p>
This samples demonstrates the use of MotionEvent properties to keep track of individual touches
This sample demonstrates the use of MotionEvent properties to keep track of individual touches
across multiple touch events.
\n\nTouch the screen with multiple fingers to show that the pointer id
(also represented by a colour) does not change as new touch events are received.</string>
(also represented by a color) does not change as new touch events are received.
</p>

Some files were not shown because too many files have changed in this diff Show More