Reputation: 926
I am making a custom watch face for Android Wear. I am having an issue with the watch face not being inflated correctly on emulators and devices with a round screen. I am able to load the round layout successfully on other Activities on the device but it is not working for the watch face activity itself.
This is my manifest file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.aegiswallet"
android:versionCode="1"
android:versionName="1.0.0">
<uses-feature android:name="android.hardware.type.watch" />
<application
android:allowBackup="true"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@android:style/Theme.DeviceDefault"
android:name=".application.AegisWearApplication">
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<activity
android:name=".activities.MyActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
android:name=".activities.WatchFaceActivity"
android:allowEmbedded="true"
android:label="Aegis Wallet">
<meta-data
android:name="com.google.android.clockwork.home.preview"
android:resource="@drawable/watchface" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.google.android.clockwork.home.category.HOME_BACKGROUND" />
</intent-filter>
</activity>
<activity android:name=".activities.ReceiveBitcoin" android:label="@string/receive_bitcoin"></activity>
<service android:name=".services.MessageListenService">
<intent-filter>
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
</intent-filter>
</service>
</application>
</manifest>
The activity "MyActivity" successfully inflates the round layout but the "WatchFaceActivity" does not. The project can be found on GitHub here: https://github.com/bsimic0001/AegisWallet/
Below is the code for each:
public class MyActivity extends Activity implements SimpleGestureFilter.SimpleGestureListener {
private SimpleGestureFilter detector;
private TextView mTextView;
private TextView balanceView;
private static final int SPEECH_REQUEST_CODE = 0;
private QRCodeWriter qrCodeWriter;
private SharedPreferences prefs;
private String address;
private ImageView addressImageView;
private Context context = this;
private int STATE_QR = 100;
private int STATE_BAL = 101;
private int CURRENT_STATE = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_my);
WatchViewStub stub = new WatchViewStub(this);
stub.setRectLayout(R.layout.rect_activity_my);
stub.setRoundLayout(R.layout.round_activity_my);
detector = new SimpleGestureFilter(this, this);
stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
@Override
public void onLayoutInflated(WatchViewStub stub) {
View rootView = stub.getRootView();
mTextView = (TextView) stub.findViewById(R.id.text);
addressImageView = (ImageView) stub.findViewById(R.id.address_image);
balanceView = (TextView) stub.findViewById(R.id.balance_text);
prefs = PreferenceManager.getDefaultSharedPreferences(context);
address = prefs.getString("ADDRESS", null);
if (address != null && addressImageView != null) {
qrCodeWriter = new QRCodeWriter();
Bitmap addressBitmap = encodeAsBitmap(address, BarcodeFormat.QR_CODE, 200);
addressImageView.setImageBitmap(addressBitmap);
addressImageView.setVisibility(View.VISIBLE);
CURRENT_STATE = STATE_QR;
} else {
Log.d("MAINACTIVITY", "address image view must be null");
}
rootView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
@Override
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
final boolean round = insets.isRound();
Log.d("MainActivity", "Is the screen round: " + round);
return insets;
}
});
}
});
setContentView(stub);
//displaySpeechRecognizer();
}
// Create an intent that can start the Speech Recognizer activity
private void displaySpeechRecognizer() {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
// Start the activity, the intent will be populated with the speech text
startActivityForResult(intent, SPEECH_REQUEST_CODE);
}
// This callback is invoked when the Speech Recognizer returns.
// This is where you process the intent and extract the speech text from the intent.
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == SPEECH_REQUEST_CODE && resultCode == RESULT_OK) {
List<String> results = data.getStringArrayListExtra(
RecognizerIntent.EXTRA_RESULTS);
String spokenText = results.get(0);
Log.d("Main", "spoken text: " + spokenText);
}
super.onActivityResult(requestCode, resultCode, data);
}
public Bitmap encodeAsBitmap(String contents, BarcodeFormat format, int dimension) {
Bitmap bitmap = null;
try {
final Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
hints.put(EncodeHintType.MARGIN, 1);
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
final BitMatrix result = qrCodeWriter.encode(contents, BarcodeFormat.QR_CODE, dimension, dimension, hints);
final int width = result.getWidth();
final int height = result.getHeight();
final int[] pixels = new int[width * height];
for (int y = 0; y < height; y++) {
final int offset = y * width;
for (int x = 0; x < width; x++) {
pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE;
}
}
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
} catch (WriterException e) {
Log.e("Basic Utils", "cannot write to bitmap " + e.getMessage());
}
return bitmap;
}
@Override
public boolean dispatchTouchEvent(MotionEvent me) {
// Call onTouchEvent of SimpleGestureFilter class
this.detector.onTouchEvent(me);
return super.dispatchTouchEvent(me);
}
@Override
public void onSwipe(int direction) {
String str = "";
switch (direction) {
case SimpleGestureFilter.SWIPE_RIGHT:
str = "Swipe Right";
break;
case SimpleGestureFilter.SWIPE_LEFT:
str = "Swipe Left";
break;
case SimpleGestureFilter.SWIPE_DOWN:
str = "Swipe Down";
handleSwipe(SimpleGestureFilter.SWIPE_DOWN);
break;
case SimpleGestureFilter.SWIPE_UP:
str = "Swipe Up";
handleSwipe(SimpleGestureFilter.SWIPE_UP);
break;
}
}
@Override
public void onDoubleTap() {
Log.d("MyActivity", "Double Tap");
}
private void handleSwipe(int swipeType){
if(CURRENT_STATE == STATE_QR){
addressImageView.setVisibility(View.GONE);
balanceView.setVisibility(View.VISIBLE);
CURRENT_STATE = STATE_BAL;
balanceView.setText("WALLET BALANCE:\n" + prefs.getString("BALANCE", "Balance not Synced"));
}
else if(CURRENT_STATE == STATE_BAL){
addressImageView.setVisibility(View.VISIBLE);
balanceView.setVisibility(View.GONE);
CURRENT_STATE = STATE_QR;
}
}
}
public class WatchFaceActivity extends Activity {
private final static IntentFilter intentFilter;
private boolean isDimmed = false;
private String TAG = "AegisWearWatchFace";
private Handler mHandler;
TextView time;
TextView timeAmPm;
TextView btcValue;
TextView walletBalance;
SharedPreferences prefs;
static {
intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_TIME_TICK);
intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
intentFilter.addAction(Intent.ACTION_TIME_CHANGED);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "inside the watch face activity");
WatchViewStub stub = new WatchViewStub(this);
stub.setRectLayout(R.layout.rect_activity_watch_face);
stub.setRoundLayout(R.layout.round_activity_watch_face);
stub.requestApplyInsets();
stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
@Override
public void onLayoutInflated(WatchViewStub stub) {
Log.d(TAG, "Layout is inflated!");
time = (TextView) stub.findViewById(R.id.time);
timeAmPm = (TextView) stub.findViewById(R.id.time_ampm);
btcValue = (TextView) stub.findViewById(R.id.bitcoin_value);
walletBalance = (TextView) stub.findViewById(R.id.wallet_balance);
View rootView = stub.getRootView();
rootView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
@Override
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
final boolean round = insets.isRound();
Log.d("WatchFaceActivity", "Is the screen round: " + round);
return insets;
}
});
}
});
setContentView(stub);
prefs = PreferenceManager.getDefaultSharedPreferences(this);
timeInfoReceiver.onReceive(this, registerReceiver(null, intentFilter));
registerReceiver(timeInfoReceiver, intentFilter);
}
public BroadcastReceiver timeInfoReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context arg0, Intent intent) {
Log.v("WatchFace", "timeChanged();");
updateLayout();
}
};
@Override
protected void onPause() {
super.onPause();
isDimmed = true;
Log.d(TAG, "dimmed");
updateLayout();
}
@Override
protected void onResume() {
super.onResume();
isDimmed = false;
Log.d(TAG, "not dimmed");
updateLayout();
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(timeInfoReceiver);
}
public void updateLayout() {
Calendar calendar = Calendar.getInstance();
int hour = calendar.get(Calendar.HOUR);
int minute = calendar.get(Calendar.MINUTE);
String am_pm;
if(calendar.get(Calendar.AM_PM) == 0) am_pm = "AM"; else am_pm = "PM";
String btcValueString = prefs.getString("BTCAMOUNT", "");
String hourText = hour + "";
if(hour == 0)
hour = 12;
if(hour < 10)
hourText = "0" + hourText;
String minuteText = minute + "";
if(minute < 10)
minuteText = "0" + minuteText;
if(time != null) {
time.setText(hourText + ":" + minuteText);
timeAmPm.setText(am_pm);
btcValue.setText(btcValueString);
walletBalance.setText(prefs.getString("BALANCE", ""));
}
else {
Log.d("WatchFace", "time is null for some reason...");
}
}
}
Upvotes: 1
Views: 843
Reputation: 544
Until the new andrioid smartwatch SDK is realeased, you can't use setOnApplyWindowInsetsListener / onApplyWindowInsets on custom watch face. This functionality only works on smartwatch app's.
To know if the clock face is round, you can use:
public static boolean heightSameAsWidth(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
int width = metrics.widthPixels;
int height = metrics.heightPixels;
return height == width;
}
private void checkIfWatchIsRound() {
if (heightSameAsWidth(getApplicationContext())) {
isRound = false;
} else {
isRound = true;
}
}
Upvotes: 0
Reputation: 1856
Try setting stub.onApplyWindowInsets(insets) inside your onApplyWindowInsets() method.
Upvotes: 1