Skip to content

Commit

Permalink
Add support for account login cluster for content apps. (#24628)
Browse files Browse the repository at this point in the history
* Add support for account login cluster for content apps.

* Use JniUtfString.

Co-authored-by: chrisdecenzo <[email protected]>
  • Loading branch information
amitnj and chrisdecenzo authored Jan 26, 2023
1 parent dfa7c9d commit 0c2b095
Show file tree
Hide file tree
Showing 11 changed files with 473 additions and 125 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,38 @@
* the rest of the media clusters TODO : Maybe generate using ZAP tool
*/
public class Clusters {

// Clusters
public static class AccountLogin {
public static final int Id = 0x050E;

public static class Commands {
public static class GetSetupPIN {
public static final int ID = 0x00;

public static class Fields {
public static final int TempAccountIdentifier = 0x00;
}
}

public static class GetSetupPINResponse {
public static final int ID = 0x01;

public static class Fields {
public static final int SetupPIN = 0x00;
}
}

public static class Login {
public static final int ID = 0x02;
}

public static class Logout {
public static final int ID = 0x03;
}
}
}

public static class MediaPlayback {
public static final int Id = 0x0506;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.example.contentapp;

import android.util.Log;
import com.matter.tv.app.api.Clusters;
import java.util.HashMap;
import java.util.Map;

/** Class to hold attribute values to help test attribute read and subscribe use cases. */
public class CommandResponseHolder {
private Map<Long, Map<Long, String>> responseValues = new HashMap<>();
private static final String TAG = "CommandResponseHolder";
private static final Long DEFAULT_COMMAND = -1L;

private static CommandResponseHolder instance = new CommandResponseHolder();

private CommandResponseHolder() {
// Setting up responses
setResponseValue(
Clusters.ContentLauncher.Id,
DEFAULT_COMMAND,
"{\"0\":0, \"1\":\"custom response from content app for content launcher\"}");
setResponseValue(
Clusters.TargetNavigator.Id,
DEFAULT_COMMAND,
"{\"0\":0, \"1\":\"custom response from content app for target navigator\"}");
setResponseValue(
Clusters.MediaPlayback.Id,
DEFAULT_COMMAND,
"{\"0\":0, \"1\":\"custom response from content app for media playback\"}");
setResponseValue(
Clusters.AccountLogin.Id,
Clusters.AccountLogin.Commands.GetSetupPIN.ID,
"{\"0\":\"12345678\"}");
};

public static CommandResponseHolder getInstance() {
return instance;
}

public void setResponseValue(long clusterId, long commandId, String value) {
if (value == null) {
Log.d(TAG, "Setting null for cluster " + clusterId + " command " + commandId);
}
Map<Long, String> responses = responseValues.get(clusterId);
if (responses == null) {
responses = new HashMap<>();
responseValues.put(clusterId, responses);
}
responses.put(commandId, value);
}

public String getCommandResponse(long clusterId, long commandId) {
Map<Long, String> responses = responseValues.get(clusterId);
String response = responses.get(commandId);
if (response == null) {
response = responses.get(DEFAULT_COMMAND);
}
return response;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
Expand All @@ -27,6 +28,7 @@ public class MainActivity extends AppCompatActivity {
private static final String ATTR_TL_LONG = "Target List : LONG";
private static final String ATTR_TL_SHORT = "Target List : SHORT";
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
private String setupPIN = "";

@Override
protected void onCreate(Bundle savedInstanceState) {
Expand All @@ -43,9 +45,30 @@ protected void onCreate(Bundle savedInstanceState) {
TextView textView = (TextView) findViewById(R.id.commandTextView);
textView.setText("Command Payload : " + command);

Button sendMessageButton = findViewById(R.id.sendMessageButton);
Button setupPINButton = findViewById(R.id.setupPINButton);
if (!setupPIN.isEmpty()) {
EditText pinText = findViewById(R.id.setupPINText);
pinText.setText(setupPIN);
}
setupPINButton.setOnClickListener(
view -> {
EditText pinText = findViewById(R.id.setupPINText);
String pinStr = pinText.getText().toString();
setupPIN = pinStr;
CommandResponseHolder.getInstance()
.setResponseValue(
Clusters.AccountLogin.Id,
Clusters.AccountLogin.Commands.GetSetupPIN.ID,
"{\""
+ Clusters.AccountLogin.Commands.GetSetupPINResponse.Fields.SetupPIN
+ "\":\""
+ pinStr
+ "\"}");
});

Button attributeUpdateButton = findViewById(R.id.updateAttributeButton);

sendMessageButton.setOnClickListener(
attributeUpdateButton.setOnClickListener(
view -> {
Spinner dropdown = findViewById(R.id.spinnerAttribute);
String attribute = (String) dropdown.getSelectedItem();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import android.content.Intent;
import android.util.Log;
import com.example.contentapp.AttributeHolder;
import com.example.contentapp.CommandResponseHolder;
import com.example.contentapp.MainActivity;
import com.example.contentapp.matter.MatterAgentClient;
import com.matter.tv.app.api.MatterIntentConstants;
Expand Down Expand Up @@ -43,7 +44,8 @@ public void onReceive(Context context, Intent intent) {
.append(command)
.toString();
Log.d(TAG, message);
String response = "{\"0\":1, \"1\":\"custom response from content app\"}";
String response =
CommandResponseHolder.getInstance().getCommandResponse(clusterId, commandId);

Intent in = new Intent(context, MainActivity.class);
in.putExtra(MatterIntentConstants.EXTRA_COMMAND_PAYLOAD, command);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,55 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<TextView
android:id="@+id/helloTextView"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textSize="10pt"
app:layout_constraintBottom_toTopOf="@+id/commandTextView"
app:layout_constraintTop_toTopOf="parent" />
android:gravity="left"
android:orientation="vertical"
android:padding="10sp">

<TextView
android:id="@+id/commandTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/helloTextView"
android:textSize="10pt"
android:text="No Command!!"
app:layout_constraintBottom_toTopOf="@+id/sendMessageButton"
app:layout_constraintTop_toBottomOf="@+id/helloTextView" />

<Spinner
android:id="@+id/spinnerAttribute"
android:layout_width="239dp"
android:layout_height="40dp"
android:background="@android:drawable/btn_dropdown"
android:spinnerMode="dropdown"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/sendMessageButton"
app:layout_constraintTop_toBottomOf="@+id/commandTextView" />


<Button
android:id="@+id/sendMessageButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/helloTextView"
android:text="Update"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/spinnerAttribute"
app:layout_constraintTop_toBottomOf="@+id/commandTextView" />

<androidx.constraintlayout.widget.Group
android:id="@+id/group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="spinnerAttribute,sendMessageButton" />
<TextView
android:id="@+id/helloTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintBottom_toTopOf="@+id/commandTextView"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/commandTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/helloTextView"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:text="No Command!!"
app:layout_constraintBottom_toTopOf="@+id/updateAttributeButton"
app:layout_constraintTop_toBottomOf="@+id/helloTextView" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10sp">

<Spinner
android:layout_width="20mm"
android:layout_height="wrap_content"
android:id="@+id/spinnerAttribute"
android:background="@android:drawable/btn_dropdown"
android:spinnerMode="dropdown" />


<Button
android:id="@+id/updateAttributeButton"
android:layout_width="20mm"
android:layout_height="wrap_content"
android:layout_marginEnd="388dp"
android:layout_toEndOf="@id/helloTextView"
android:text="Update Attribute" />

</LinearLayout>

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10sp">

<EditText
android:inputType="number"
android:id="@+id/setupPINText"
android:layout_width="20mm"
android:layout_height="wrap_content"
android:text="20202021"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />

<Button
android:id="@+id/setupPINButton"
android:layout_width="20mm"
android:layout_height="wrap_content"
android:text="Update PIN" />

</LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</FrameLayout>
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,24 @@
#include "AccountLoginManager.h"
#include <app/CommandHandler.h>
#include <app/util/af.h>
#include <json/json.h>
#include <lib/core/DataModelTypes.h>

using namespace std;
using namespace chip::app::Clusters::AccountLogin;
using Status = chip::Protocols::InteractionModel::Status;

AccountLoginManager::AccountLoginManager(const char * setupPin)
AccountLoginManager::AccountLoginManager(ContentAppCommandDelegate * commandDelegate, const char * setupPin) :
mCommandDelegate(commandDelegate)
{
CopyString(mSetupPin, sizeof(mSetupPin), setupPin);
}

bool AccountLoginManager::HandleLogin(const CharSpan & tempAccountIdentifier, const CharSpan & setupPin)
{
ChipLogProgress(DeviceLayer, "AccountLoginManager::HandleLogin called for endpoint %d", mEndpointId);
string tempAccountIdentifierString(tempAccountIdentifier.data(), tempAccountIdentifier.size());
string setupPinString(setupPin.data(), setupPin.size());
ChipLogProgress(Zcl, "temporary account id: %s", tempAccountIdentifierString.c_str());
ChipLogProgress(Zcl, "setup pin %s", setupPinString.c_str());

if (strcmp(mSetupPin, setupPinString.c_str()) == 0)
{
Expand Down Expand Up @@ -64,3 +67,37 @@ void AccountLoginManager::HandleGetSetupPin(CommandResponseHelper<GetSetupPINRes
response.setupPIN = CharSpan::fromCharString(mSetupPin);
helper.Success(response);
}

void AccountLoginManager::GetSetupPin(char * setupPin, size_t setupPinSize, const CharSpan & tempAccountIdentifier)
{
// Only this method is called outside of the normal Matter communication with endpoints and clusters.
// Hence we have to introduce call to the java layer(thorough the command delegate) here.
// Other methods in this class do not need to be changed beecause those will get routed to java layer
// upstream.
ChipLogProgress(DeviceLayer, "AccountLoginManager::GetSetupPin called for endpoint %d", mEndpointId);
string tempAccountIdentifierString(tempAccountIdentifier.data(), tempAccountIdentifier.size());
if (mCommandDelegate == nullptr)
{
// For the dummy content apps to work.
CopyString(setupPin, setupPinSize, mSetupPin);
ChipLogProgress(Zcl, "Returning pin for dummy content app");
return;
}

Json::Value response;
bool commandHandled = true;
mCommandDelegate->InvokeCommand(mEndpointId, chip::app::Clusters::AccountLogin::Id,
chip::app::Clusters::AccountLogin::Commands::GetSetupPIN::Id,
"{\"0\": \"" + tempAccountIdentifierString + "\"}", commandHandled, response);
Status status;
GetSetupPINResponse pinResponse = mCommandDelegate->FormatGetSetupPINResponse(response, status);
if (status == chip::Protocols::InteractionModel::Status::Success)
{
CopyString(setupPin, setupPinSize, pinResponse.setupPIN);
}
else
{
CopyString(setupPin, setupPinSize, "");
}
ChipLogProgress(Zcl, "Returning pin for content app for endpoint %d", mEndpointId);
};
Loading

0 comments on commit 0c2b095

Please sign in to comment.