public final class

ErrorMessageView

extends LinearLayout

 java.lang.Object

↳LinearLayout

↳androidx.car.app.activity.ui.ErrorMessageView

Gradle dependencies

compile group: 'androidx.car.app', name: 'app-automotive', version: '1.2.0-rc01'

  • groupId: androidx.car.app
  • artifactId: app-automotive
  • version: 1.2.0-rc01

Artifact androidx.car.app:app-automotive:1.2.0-rc01 it located at Google repository (https://maven.google.com/)

Overview

A Fragment to show error message on CarAppActivity

Summary

Constructors
publicErrorMessageView(Context context)

publicErrorMessageView(Context context, AttributeSet attrs)

publicErrorMessageView(Context context, AttributeSet attrs, int defStyleAttr)

publicErrorMessageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)

Methods
protected voidonFinishInflate()

public voidsetError(ErrorHandler.ErrorType errorType)

Updates the error displayed by this view

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public ErrorMessageView(Context context)

public ErrorMessageView(Context context, AttributeSet attrs)

public ErrorMessageView(Context context, AttributeSet attrs, int defStyleAttr)

public ErrorMessageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)

Methods

protected void onFinishInflate()

public void setError(ErrorHandler.ErrorType errorType)

Updates the error displayed by this view

Source

/*
 * Copyright 2021 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 androidx.car.app.activity.ui;

import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.car.app.activity.ErrorHandler.ActionType.UPDATE_HOST;
import static androidx.car.app.activity.LogTags.TAG_ERROR;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.car.app.activity.CarAppActivity;
import androidx.car.app.activity.CarAppViewModel;
import androidx.car.app.activity.ErrorHandler;
import androidx.car.app.automotive.R;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.ViewModelProvider;

import java.util.List;

/**
 * A {@link Fragment} to show error message on {@link CarAppActivity}
 *
 * @hide
 */
@RestrictTo(LIBRARY)
public final class ErrorMessageView extends LinearLayout {

    private static final String VENDING_PACKAGE = "com.android.vending";
    private static final String VENDING_DETAIL_URL =
            "https://play.google.com/store/apps/details?id=";
    private static final String ACTION_RENDER = "android.car.template.host.RendererService";

    // TODO(b/194324567): Remove the hard coded Google templates host package name
    private static final String HOST_PACKAGE = "com.google.android.apps.automotive.templates.host";

    private TextView mErrorMessage;
    private Button mActionButton;
    @Nullable
    private ErrorHandler.ErrorType mErrorType;

    public ErrorMessageView(@NonNull Context context) {
        super(context);
    }

    public ErrorMessageView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public ErrorMessageView(@NonNull Context context, @Nullable AttributeSet attrs,
            int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public ErrorMessageView(@NonNull Context context, @Nullable AttributeSet attrs,
            int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mErrorMessage = requireViewById(R.id.error_message);
        mActionButton = requireViewById(R.id.action_button);
        mActionButton.setOnClickListener(v -> onClick());
    }

    /**
     * Updates the error displayed by this view
     */
    public void setError(@Nullable ErrorHandler.ErrorType errorType) {
        mErrorType = errorType;
        mErrorMessage.setText(mErrorType != null
                ? getContext().getString(mErrorType.getMessageResId())
                : null);
        mActionButton.setText(mErrorType != null
                ? getContext().getString(mErrorType.getActionType().getActionResId())
                : null);
        mActionButton.setVisibility(mErrorType != null ? View.VISIBLE : View.GONE);

        // If the vending app is not installed, hide the button and update the message.
        if (mErrorType != null && mErrorType.getActionType() == UPDATE_HOST
                && !isVendingPackageInstalled()) {
            mActionButton.setVisibility(INVISIBLE);
            mErrorMessage.setText(R.string.error_message_no_vending);
        }
    }

    private void onClick() {
        if (mErrorType == null) {
            return;
        }
        switch (mErrorType.getActionType()) {
            case UPDATE_HOST:
                getContext().startActivity(getVendingIntent());
                requireActivity().finish();
                return;
            case FINISH:
                requireActivity().finish();
                return;
            case RETRY:
                new ViewModelProvider(requireActivity())
                        .get(CarAppViewModel.class)
                        .retryBinding();
                return;
        }
        throw new IllegalArgumentException("Unknown action type: " + mErrorType.getActionType());
    }

    @SuppressWarnings("deprecation")
    private boolean isVendingPackageInstalled() {
        try {
            requireActivity().getPackageManager().getPackageInfo(VENDING_PACKAGE, 0);
        } catch (NameNotFoundException e) {
            Log.d(TAG_ERROR, "The vending app not found");
            return false;
        }
        return true;
    }

    @SuppressWarnings("deprecation")
    private Intent getVendingIntent() {
        Intent rendererIntent = new Intent(ACTION_RENDER);
        List<ResolveInfo> resolveInfoList =
                requireActivity().getPackageManager().queryIntentServices(
                        rendererIntent,
                        PackageManager.GET_META_DATA
                );
        // Redirect to the vending app package detail if only one package that handles
        // ACTION_RENDER is found.
        // Redirect to GAS host page if found no package that handles ACTION_RENDER.
        // if found multiple or none, redirect to the vending app main page.
        if (resolveInfoList.size() == 1) {
            Log.d(TAG_ERROR, "Find a host, redirect to the page for this host.");
            return getHostPageIntent(resolveInfoList.get(0).serviceInfo.packageName);
        } else if (resolveInfoList.size() == 0) {
            Log.d(TAG_ERROR, "No host found on the device, redirect to GAS host page");
            return getHostPageIntent(HOST_PACKAGE);
        } else {
            Log.d(TAG_ERROR, "Multiple host found, redirect to the vending app main page");
            return requireActivity().getPackageManager().getLaunchIntentForPackage(VENDING_PACKAGE);
        }
    }

    private Intent getHostPageIntent(String packageName) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setPackage(VENDING_PACKAGE);
        intent.setData(Uri.parse(VENDING_DETAIL_URL + packageName));
        return intent;
    }

    private FragmentActivity requireActivity() {
        return (FragmentActivity) getContext();
    }
}