blob: 3f1e1919bf0c83e16f2c7a94cc7309e7328167fa [file] [log] [blame]
/*
* Copyright (c) 2016, 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.android.car.overview;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.Toast;
import com.android.car.stream.IStreamConsumer;
import com.android.car.stream.IStreamService;
import com.android.car.stream.StreamCard;
import com.android.car.stream.StreamConstants;
import com.android.car.view.PagedListView;
import java.util.List;
/**
* An overview activity that presents {@link StreamCard} as scrollable list.
*/
public class StreamOverviewActivity extends Activity {
private static final String TAG = "Overview";
private static final int SERVICE_CONNECTION_RETRY_DELAY_MS = 5000;
private static final String ACTION_CAR_OVERVIEW_STATE_CHANGE
= "android.intent.action.CAR_OVERVIEW_APP_STATE_CHANGE";
private static final String EXTRA_CAR_OVERVIEW_FOREGROUND
= "android.intent.action.CAR_APP_STATE";
private static final int PERMISSION_ACTIVITY_REQUEST_CODE = 5151;
private PagedListView mPageListView;
private View mPermissionText;
private StreamAdapter mAdapter;
private final Handler mHandler = new Handler();
private int mConnectionRetryCount;
private IStreamService mService;
private StreamServiceConnection mConnection;
private int mCardBottomMargin;
private Toast mToast;
boolean mCheckPermissionsOnResume;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.overview_activity);
mCardBottomMargin = getResources().getDimensionPixelSize(R.dimen.stream_card_bottom_margin);
int statusBarHeight = getStatusBarHeight();
FrameLayout.LayoutParams params
= (FrameLayout.LayoutParams) findViewById(R.id.action_icon_bar).getLayoutParams();
params.setMargins(0, statusBarHeight, 0, 0);
mAdapter = new StreamAdapter(this /* context */);
mPageListView = (PagedListView) findViewById(R.id.list_view);
mPageListView.setAdapter(mAdapter);
mPageListView.addItemDecoration(new DefaultDecoration());
mPageListView.setLightMode();
int listTopMargin = statusBarHeight
+ getResources().getDimensionPixelSize(R.dimen.lens_header_height);
FrameLayout.LayoutParams listViewParams
= (FrameLayout.LayoutParams) mPageListView.getLayoutParams();
listViewParams.setMargins(0, listTopMargin, 0, 0);
mPermissionText = findViewById(R.id.permission_text);
mPermissionText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startPermissionsActivity(false /* checkPermissionsOnly */);
}
});
mToast = Toast.makeText(StreamOverviewActivity.this,
getString(R.string.voice_assistant_help_msg), Toast.LENGTH_SHORT);
mToast.setGravity(Gravity.CENTER, 0, 0);
findViewById(R.id.voice_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mToast.show();
}
});
findViewById(R.id.gear_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS));
}
});
startPermissionsActivity(true /* checkPermissionsOnly */);
}
private void startPermissionsActivity(boolean checkPermissionsOnly) {
// Start StreamService's permission activity before binding to it.
Intent intent = new Intent();
intent.setComponent(new ComponentName(
getString(R.string.car_stream_item_manager_package_name),
getString(R.string.car_stream_item_manager_permissions_activity)));
intent.putExtra(StreamConstants.STREAM_PERMISSION_CHECK_PERMISSIONS_ONLY,
checkPermissionsOnly);
startActivityForResult(intent, PERMISSION_ACTIVITY_REQUEST_CODE);
}
@Override
protected void onResume() {
super.onResume();
if (mCheckPermissionsOnResume) {
startPermissionsActivity(true /* checkPermissionsOnly */);
} else {
mCheckPermissionsOnResume = true;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PERMISSION_ACTIVITY_REQUEST_CODE) {
// onResume is called after onActivityResult, if the permissions activity has
// already finished, then don't bother checking for permissions again on resume.
mCheckPermissionsOnResume = false;
if (resultCode == Activity.RESULT_OK) {
mPermissionText.setVisibility(View.GONE);
mPageListView.setVisibility(View.VISIBLE);
bindStreamService();
} else {
mPermissionText.setVisibility(View.VISIBLE);
mPageListView.setVisibility(View.GONE);
}
}
}
@Override
protected void onDestroy() {
// Do a tear down to avoid leaks
mHandler.removeCallbacks(mServiceConnectionRetry);
if (mConnection != null) {
unbindService(mConnection);
}
super.onDestroy();
}
@Override
protected void onStart() {
super.onStart();
Intent i = new Intent(ACTION_CAR_OVERVIEW_STATE_CHANGE);
i.putExtra(EXTRA_CAR_OVERVIEW_FOREGROUND, true);
sendBroadcast(i);
}
@Override
protected void onStop() {
Intent i = new Intent(ACTION_CAR_OVERVIEW_STATE_CHANGE);
i.putExtra(EXTRA_CAR_OVERVIEW_FOREGROUND, false);
sendBroadcast(i);
super.onStop();
}
private class StreamServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mConnectionRetryCount = 1;
// If there is currently a retry scheduled, cancel it.
if (mServiceConnectionRetry != null) {
mHandler.removeCallbacks(mServiceConnectionRetry);
}
mService = IStreamService.Stub.asInterface(service);
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Service connected");
}
try {
mService.registerConsumer(mStreamConsumer);
List<StreamCard> cards = mService.fetchAllStreamCards();
if (cards != null) {
for (StreamCard card : cards) {
mAdapter.addCard(card);
}
}
} catch (RemoteException e) {
throw new IllegalStateException("not connected to IStreamItemManagerService");
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
mAdapter.removeAllCards();
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Service disconnected, reconnecting...");
}
mHandler.removeCallbacks(mServiceConnectionRetry);
mHandler.postDelayed(mServiceConnectionRetry, SERVICE_CONNECTION_RETRY_DELAY_MS);
}
}
private Runnable mServiceConnectionRetry = new Runnable() {
@Override
public void run() {
if (mService != null) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Stream service rebound by framework, no need to bind again");
}
return;
}
mConnectionRetryCount++;
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Rebinding disconnected Stream Service, retry count: "
+ mConnectionRetryCount);
}
if (!bindStreamService()) {
mHandler.postDelayed(mServiceConnectionRetry,
mConnectionRetryCount * SERVICE_CONNECTION_RETRY_DELAY_MS);
}
}
};
private final IStreamConsumer mStreamConsumer = new IStreamConsumer.Stub() {
@Override
public void onStreamCardAdded(StreamCard card) throws RemoteException {
StreamOverviewActivity.this.runOnUiThread(new Runnable() {
public void run() {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Stream Card added: " + card);
}
mAdapter.addCard(card);
}
});
}
@Override
public void onStreamCardRemoved(StreamCard card) throws RemoteException {
StreamOverviewActivity.this.runOnUiThread(new Runnable() {
public void run() {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Stream Card removed: " + card);
}
mAdapter.removeCard(card);
}
});
}
@Override
public void onStreamCardChanged(StreamCard newStreamCard) throws RemoteException {
}
};
private boolean bindStreamService() {
mConnection = new StreamServiceConnection();
Intent intent = new Intent();
intent.setAction(StreamConstants.STREAM_CONSUMER_BIND_ACTION);
intent.setComponent(new ComponentName(
getString(R.string.car_stream_item_manager_package_name),
getString(R.string.car_stream_item_manager_class_name)));
boolean bound = bindService(intent, mConnection, BIND_AUTO_CREATE);
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "IStreamItemManagerService bound: " + bound
+ "; component: " + intent.getComponent());
}
return bound;
}
private class DefaultDecoration extends RecyclerView.ItemDecoration {
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
outRect.bottom = mCardBottomMargin;
}
}
private int getStatusBarHeight() {
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
}